diff --git a/django/core/management/__init__.py b/django/core/management/__init__.py
index 019837b35c..c9ccd6a370 100644
--- a/django/core/management/__init__.py
+++ b/django/core/management/__init__.py
@@ -41,6 +41,9 @@ def find_management_module(app_name):
 
     Raises ImportError if the management module cannot be found for any reason.
     """
+    # TODO: this method is only called from get_commands() which has already
+    # imported the application module at that point.
+
     parts = app_name.split('.')
     parts.append('management')
     parts.reverse()
@@ -108,7 +111,7 @@ def get_commands():
 
         # Find the installed apps
         try:
-            settings.INSTALLED_APPS
+            installed_apps = settings.INSTALLED_APPS
         except ImproperlyConfigured:
             # Still useful for commands that do not require functional
             # settings, like startproject or help.
@@ -118,6 +121,8 @@ def get_commands():
             # avoid catching ImproperlyConfigured errors that aren't caused
             # by the absence of a settings module.
             from django.apps import apps
+            apps.populate_apps(installed_apps)
+            apps.populate_models()
             app_configs = apps.get_app_configs()
             app_names = [app_config.name for app_config in app_configs]
 
diff --git a/django/core/management/base.py b/django/core/management/base.py
index 413592a8f3..a71cc78588 100644
--- a/django/core/management/base.py
+++ b/django/core/management/base.py
@@ -344,11 +344,9 @@ class AppCommand(BaseCommand):
         from django.apps import apps
         if not app_labels:
             raise CommandError("Enter at least one application label.")
-        # Populate models and don't use only_with_models_module=True when
-        # calling get_app_config() to tell apart missing apps from apps
-        # without a model module -- which can't be supported with the legacy
-        # API since it passes the models module to handle_app().
-        apps.populate_models()
+        # Don't use only_with_models_module=True in get_app_config() to tell
+        # apart missing apps from apps without a model module -- which can't
+        # be supported with the legacy API since it passes the models module.
         try:
             app_configs = [apps.get_app_config(app_label) for app_label in app_labels]
         except (LookupError, ImportError) as e:
diff --git a/django/core/management/commands/shell.py b/django/core/management/commands/shell.py
index eea233bcff..5ef51db3f9 100644
--- a/django/core/management/commands/shell.py
+++ b/django/core/management/commands/shell.py
@@ -1,6 +1,7 @@
-import os
-from django.core.management.base import NoArgsCommand
 from optparse import make_option
+import os
+
+from django.core.management.base import NoArgsCommand
 
 
 class Command(NoArgsCommand):
@@ -64,11 +65,6 @@ class Command(NoArgsCommand):
         raise ImportError
 
     def handle_noargs(self, **options):
-        # XXX: (Temporary) workaround for ticket #1796: force early loading of all
-        # models from installed apps.
-        from django.apps import apps
-        apps.populate_models()
-
         use_plain = options.get('plain', False)
         no_startup = options.get('no_startup', False)
         interface = options.get('interface', None)
diff --git a/django/core/wsgi.py b/django/core/wsgi.py
index edea3334c9..d2dadee623 100644
--- a/django/core/wsgi.py
+++ b/django/core/wsgi.py
@@ -1,3 +1,5 @@
+from django.apps import apps
+from django.conf import settings
 from django.core.handlers.wsgi import WSGIHandler
 
 
@@ -10,4 +12,9 @@ def get_wsgi_application():
     case the internal WSGI implementation changes or moves in the future.
 
     """
+    # Configure the settings (this happens automatically on the first access).
+    # Populate the app registry.
+    apps.populate_apps(settings.INSTALLED_APPS)
+    apps.populate_models()
+
     return WSGIHandler()