diff --git a/django/apps/config.py b/django/apps/config.py
index dd1018b50a..9cfe4858aa 100644
--- a/django/apps/config.py
+++ b/django/apps/config.py
@@ -1,7 +1,7 @@
 from importlib import import_module
 import os
 
-from django.core.exceptions import ImproperlyConfigured
+from django.core.exceptions import AppRegistryNotReady, ImproperlyConfigured
 from django.utils.module_loading import module_has_submodule
 from django.utils._os import upath
 
@@ -139,15 +139,21 @@ class AppConfig(object):
         # Entry is a path to an app config class.
         return cls(app_name, app_module)
 
+    def check_models_ready(self):
+        """
+        Raises an exception if models haven't been imported yet.
+        """
+        if self.models is None:
+            raise AppRegistryNotReady(
+                "Models for app '%s' haven't been imported yet." % self.label)
+
     def get_model(self, model_name):
         """
         Returns the model with the given case-insensitive model_name.
 
         Raises LookupError if no model exists with this name.
         """
-        if self.models is None:
-            raise LookupError(
-                "App '%s' doesn't have any models." % self.label)
+        self.check_models_ready()
         try:
             return self.models[model_name.lower()]
         except KeyError:
@@ -169,6 +175,7 @@ class AppConfig(object):
         Set the corresponding keyword argument to True to include such models.
         Keyword arguments aren't documented; they're a private API.
         """
+        self.check_models_ready()
         for model in self.models.values():
             if model._deferred and not include_deferred:
                 continue
diff --git a/django/apps/registry.py b/django/apps/registry.py
index 492a97f603..57bdc3edb2 100644
--- a/django/apps/registry.py
+++ b/django/apps/registry.py
@@ -43,7 +43,7 @@ class Apps(object):
         self.stored_app_configs = []
 
         # Whether the registry is populated.
-        self.ready = False
+        self.apps_ready = self.models_ready = self.ready = False
 
         # Lock for thread-safe population.
         self._lock = threading.Lock()
@@ -100,29 +100,41 @@ class Apps(object):
                     "Application names aren't unique, "
                     "duplicates: %s" % ", ".join(duplicates))
 
+            self.apps_ready = True
+
             # Load models.
             for app_config in self.app_configs.values():
                 all_models = self.all_models[app_config.label]
                 app_config.import_models(all_models)
 
             self.clear_cache()
-            self.ready = True
+
+            self.models_ready = True
 
             for app_config in self.get_app_configs():
                 app_config.ready()
 
-    def check_ready(self):
+            self.ready = True
+
+    def check_apps_ready(self):
         """
-        Raises an exception if the registry isn't ready.
+        Raises an exception if all apps haven't been imported yet.
         """
-        if not self.ready:
-            raise AppRegistryNotReady()
+        if not self.apps_ready:
+            raise AppRegistryNotReady("Apps aren't loaded yet.")
+
+    def check_models_ready(self):
+        """
+        Raises an exception if all models haven't been imported yet.
+        """
+        if not self.models_ready:
+            raise AppRegistryNotReady("Models aren't loaded yet.")
 
     def get_app_configs(self):
         """
         Imports applications and returns an iterable of app configs.
         """
-        self.check_ready()
+        self.check_apps_ready()
         return self.app_configs.values()
 
     def get_app_config(self, app_label):
@@ -131,7 +143,7 @@ class Apps(object):
 
         Raises LookupError if no application exists with this label.
         """
-        self.check_ready()
+        self.check_apps_ready()
         try:
             return self.app_configs[app_label]
         except KeyError:
@@ -153,7 +165,7 @@ class Apps(object):
 
         Set the corresponding keyword argument to True to include such models.
         """
-        self.check_ready()
+        self.check_models_ready()
         if app_mod:
             warnings.warn(
                 "The app_mod argument of get_models is deprecated.",
@@ -184,7 +196,7 @@ class Apps(object):
         model exists with this name in the application. Raises ValueError if
         called with a single argument that doesn't contain exactly one dot.
         """
-        self.check_ready()
+        self.check_models_ready()
         if model_name is None:
             app_label, model_name = app_label.split('.')
         return self.get_app_config(app_label).get_model(model_name.lower())
@@ -207,10 +219,8 @@ class Apps(object):
         Checks whether an application with this name exists in the registry.
 
         app_name is the full name of the app eg. 'django.contrib.admin'.
-
-        It's safe to call this method at import time, even while the registry
-        is being populated. It returns False for apps that aren't loaded yet.
         """
+        self.check_apps_ready()
         return any(ac.name == app_name for ac in self.app_configs.values())
 
     def get_containing_app_config(self, object_name):
@@ -221,10 +231,10 @@ class Apps(object):
 
         Returns the app config for the inner application in case of nesting.
         Returns None if the object isn't in any registered app config.
-
-        It's safe to call this method at import time, even while the registry
-        is being populated.
         """
+        # In Django 1.7 and 1.8, it's allowed to call this method at import
+        # time, even while the registry is being populated. In Django 1.9 and
+        # later, that should be forbidden with `self.check_apps_ready()`.
         candidates = []
         for app_config in self.app_configs.values():
             if object_name.startswith(app_config.name):
@@ -297,10 +307,11 @@ class Apps(object):
         imports safely (eg. that could lead to registering listeners twice),
         models are registered when they're imported and never removed.
         """
-        self.check_ready()
+        if not self.ready:
+            raise AppRegistryNotReady("App registry isn't ready yet.")
         self.stored_app_configs.append(self.app_configs)
         self.app_configs = OrderedDict()
-        self.ready = False
+        self.apps_ready = self.models_ready = self.ready = False
         self.clear_cache()
         self.populate(installed)
 
@@ -309,7 +320,7 @@ class Apps(object):
         Cancels a previous call to set_installed_apps().
         """
         self.app_configs = self.stored_app_configs.pop()
-        self.ready = True
+        self.apps_ready = self.models_ready = self.ready = True
         self.clear_cache()
 
     def clear_cache(self):
@@ -402,7 +413,7 @@ class Apps(object):
         warnings.warn(
             "[a.path for a in get_app_configs()] supersedes get_app_paths().",
             RemovedInDjango19Warning, stacklevel=2)
-        self.check_ready()
+        self.check_apps_ready()
         app_paths = []
         for app in self.get_apps():
             app_paths.append(self._get_app_path(app))
diff --git a/docs/ref/applications.txt b/docs/ref/applications.txt
index a7590b899d..f1da87a1fd 100644
--- a/docs/ref/applications.txt
+++ b/docs/ref/applications.txt
@@ -301,10 +301,6 @@ Application registry
     Checks whether an application with the given name exists in the registry.
     ``app_name`` is the full name of the app, e.g. ``'django.contrib.admin'``.
 
-    Unlike :meth:`~django.apps.apps.get_app_config`, this method can be called
-    safely at import time. If the registry is still being populated, it may
-    return ``False``, even though the app will become available later.
-
 .. method:: apps.get_model(app_label, model_name)
 
     Returns the :class:`~django.db.models.Model` with the given ``app_label``
@@ -365,6 +361,9 @@ processes all applications in the order of :setting:`INSTALLED_APPS`.
    the order of :setting:`INSTALLED_APPS`, it's strongly recommended not
    import any models at this stage.
 
+   Once this stage completes, APIs that operate of application configurations
+   such as :meth:`~apps.get_app_config()` become usable.
+
 #. Then Django attempts to import the ``models`` submodule of each application,
    if there is one.
 
@@ -372,6 +371,9 @@ processes all applications in the order of :setting:`INSTALLED_APPS`.
    ``models/__init__.py``. Otherwise, the application registry may not be fully
    populated at this point, which could cause the ORM to malfunction.
 
+   Once this stage completes, APIs that operate on models such as
+   :meth:`~apps.get_model()` become usable.
+
 #. Finally Django runs the :meth:`~AppConfig.ready()` method of each application
    configuration.