Browse Source

Fixed #16360 -- Added WSGI entrypoint to startproject layout, and enabled internal servers (runserver and runfcgi) to use an externally-defined WSGI application. Thanks to Armin Ronacher, Jannis Leidel, Alex Gaynor, ptone, and Jacob Kaplan-Moss.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@17022 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Carl Meyer 13 years ago
parent
commit
145a77edc9

+ 7 - 0
django/conf/global_settings.py

@@ -407,6 +407,13 @@ X_FRAME_OPTIONS = 'SAMEORIGIN'
 
 USE_X_FORWARDED_HOST = False
 
+# The Python dotted path to the WSGI application that Django's internal servers
+# (runserver, runfcgi) will use. If `None`, the return value of
+# 'django.core.wsgi.get_wsgi_application' is used, thus preserving the same
+# behavior as previous versions of Django. Otherwise this should point to an
+# actual WSGI application object.
+WSGI_APPLICATION = None
+
 ##############
 # MIDDLEWARE #
 ##############

+ 3 - 0
django/conf/project_template/project_name/settings.py

@@ -99,6 +99,9 @@ MIDDLEWARE_CLASSES = (
 
 ROOT_URLCONF = '{{ project_name }}.urls'
 
+# Python dotted path to the WSGI application used by Django's runserver.
+WSGI_APPLICATION = '{{ project_name }}.wsgi.application'
+
 TEMPLATE_DIRS = (
     # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
     # Always use forward slashes, even on Windows.

+ 28 - 0
django/conf/project_template/project_name/wsgi.py

@@ -0,0 +1,28 @@
+"""
+WSGI config for {{ project_name }} project.
+
+This module contains the WSGI application used by Django's development server
+and any production WSGI deployments. It should expose a module-level variable
+named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover
+this application via the ``WSGI_APPLICATION`` setting.
+
+Usually you will have the standard Django WSGI application here, but it also
+might make sense to replace the whole Django WSGI application with a custom one
+that later delegates to the Django one. For example, you could introduce WSGI
+middleware here, or combine a Django application with an application of another
+framework.
+
+"""
+import os
+
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "{{ project_name }}.settings")
+
+# This application object is used by any WSGI server configured to use this
+# file. This includes Django's development server, if the WSGI_APPLICATION
+# setting points here.
+from django.core.wsgi import get_wsgi_application
+application = get_wsgi_application()
+
+# Apply WSGI middleware here.
+# from helloworld.wsgi import HelloWorldApplication
+# application = HelloWorldApplication(application)

+ 6 - 4
django/contrib/staticfiles/management/commands/runserver.py

@@ -16,12 +16,14 @@ class Command(BaseRunserverCommand):
 
     def get_handler(self, *args, **options):
         """
-        Returns the static files serving handler.
+        Returns the static files serving handler wrapping the default handler,
+        if static files should be served. Otherwise just returns the default
+        handler.
+
         """
         handler = super(Command, self).get_handler(*args, **options)
         use_static_handler = options.get('use_static_handler', True)
         insecure_serving = options.get('insecure_serving', False)
-        if (settings.DEBUG and use_static_handler or
-                (use_static_handler and insecure_serving)):
-            handler = StaticFilesHandler(handler)
+        if use_static_handler and (settings.DEBUG or insecure_serving):
+            return StaticFilesHandler(handler)
         return handler

+ 2 - 2
django/core/handlers/base.py

@@ -242,8 +242,8 @@ def get_script_name(environ):
     Returns the equivalent of the HTTP request's SCRIPT_NAME environment
     variable. If Apache mod_rewrite has been used, returns what would have been
     the script name prior to any rewriting (so it's the script name as seen
-    from the client's perspective), unless FORCE_SCRIPT_NAME is set (to
-    anything).
+    from the client's perspective), unless the FORCE_SCRIPT_NAME setting is
+    set (to anything).
     """
     from django.conf import settings
     if settings.FORCE_SCRIPT_NAME is not None:

+ 2 - 3
django/core/handlers/wsgi.py

@@ -124,6 +124,7 @@ class LimitedStream(object):
         self.buffer = sio.read()
         return line
 
+
 class WSGIRequest(http.HttpRequest):
     def __init__(self, environ):
         script_name = base.get_script_name(environ)
@@ -202,13 +203,12 @@ class WSGIRequest(http.HttpRequest):
     FILES = property(_get_files)
     REQUEST = property(_get_request)
 
+
 class WSGIHandler(base.BaseHandler):
     initLock = Lock()
     request_class = WSGIRequest
 
     def __call__(self, environ, start_response):
-        from django.conf import settings
-
         # Set up middleware if needed. We couldn't do this earlier, because
         # settings weren't available.
         if self._request_middleware is None:
@@ -253,4 +253,3 @@ class WSGIHandler(base.BaseHandler):
             response_headers.append(('Set-Cookie', str(c.output(header=''))))
         start_response(status, response_headers)
         return response
-

+ 2 - 3
django/core/management/commands/runserver.py

@@ -5,8 +5,7 @@ import sys
 import socket
 
 from django.core.management.base import BaseCommand, CommandError
-from django.core.handlers.wsgi import WSGIHandler
-from django.core.servers.basehttp import AdminMediaHandler, run, WSGIServerException
+from django.core.servers.basehttp import AdminMediaHandler, run, WSGIServerException, get_internal_wsgi_application
 from django.utils import autoreload
 
 naiveip_re = re.compile(r"""^(?:
@@ -37,7 +36,7 @@ class BaseRunserverCommand(BaseCommand):
         """
         Returns the default WSGI handler for the runner.
         """
-        return WSGIHandler()
+        return get_internal_wsgi_application()
 
     def handle(self, addrport='', *args, **options):
         self.use_ipv6 = options.get('use_ipv6')

+ 40 - 0
django/core/servers/basehttp.py

@@ -18,7 +18,10 @@ from wsgiref import simple_server
 from wsgiref.util import FileWrapper   # for backwards compatibility
 
 import django
+from django.core.exceptions import ImproperlyConfigured
 from django.core.management.color import color_style
+from django.core.wsgi import get_wsgi_application
+from django.utils.importlib import import_module
 from django.utils._os import safe_join
 from django.views import static
 
@@ -27,6 +30,43 @@ from django.contrib.staticfiles import handlers
 __all__ = ['WSGIServer', 'WSGIRequestHandler']
 
 
+def get_internal_wsgi_application():
+    """
+    Loads and returns the WSGI application as configured by the user in
+    ``settings.WSGI_APPLICATION``. With the default ``startproject`` layout,
+    this will be the ``application`` object in ``projectname/wsgi.py``.
+
+    This function, and the ``WSGI_APPLICATION`` setting itself, are only useful
+    for Django's internal servers (runserver, runfcgi); external WSGI servers
+    should just be configured to point to the correct application object
+    directly.
+
+    If settings.WSGI_APPLICATION is not set (is ``None``), we just return
+    whatever ``django.core.wsgi.get_wsgi_application`` returns.
+
+    """
+    from django.conf import settings
+    app_path = getattr(settings, 'WSGI_APPLICATION')
+    if app_path is None:
+        return get_wsgi_application()
+    module_name, attr = app_path.rsplit('.', 1)
+    try:
+        mod = import_module(module_name)
+    except ImportError, e:
+        raise ImproperlyConfigured(
+            "WSGI application '%s' could not be loaded; "
+            "could not import module '%s': %s" % (app_path, module_name, e))
+    try:
+        app = getattr(mod, attr)
+    except AttributeError, e:
+        raise ImproperlyConfigured(
+            "WSGI application '%s' could not be loaded; "
+            "can't find '%s' in module '%s': %s"
+            % (app_path, attr, module_name, e))
+
+    return app
+
+
 class WSGIServerException(Exception):
     pass
 

+ 2 - 2
django/core/servers/fastcgi.py

@@ -139,7 +139,7 @@ def runfastcgi(argset=[], **kwargs):
         return False
 
     # Prep up and go
-    from django.core.handlers.wsgi import WSGIHandler
+    from django.core.servers.basehttp import get_internal_wsgi_application
 
     if options["host"] and options["port"] and not options["socket"]:
         wsgi_opts['bindAddress'] = (options["host"], int(options["port"]))
@@ -178,7 +178,7 @@ def runfastcgi(argset=[], **kwargs):
         fp.write("%d\n" % os.getpid())
         fp.close()
 
-    WSGIServer(WSGIHandler(), **wsgi_opts).run()
+    WSGIServer(get_internal_wsgi_application(), **wsgi_opts).run()
 
 if __name__ == '__main__':
     runfastcgi(sys.argv[1:])

+ 13 - 0
django/core/wsgi.py

@@ -0,0 +1,13 @@
+from django.core.handlers.wsgi import WSGIHandler
+
+
+def get_wsgi_application():
+    """
+    The public interface to Django's WSGI support. Should return a WSGI
+    callable.
+
+    Allows us to avoid making django.core.handlers.WSGIHandler public API, in
+    case the internal WSGI implementation changes or moves in the future.
+
+    """
+    return WSGIHandler()

+ 1 - 1
docs/faq/install.txt

@@ -23,7 +23,7 @@ usage.
 For a development environment -- if you just want to experiment with Django --
 you don't need to have a separate Web server installed; Django comes with its
 own lightweight development server. For a production environment, Django follows
-the WSGI spec, :pep:`333`, which means it can run on a variety of server
+the WSGI spec, :pep:`3333`, which means it can run on a variety of server
 platforms. See :doc:`Deploying Django </howto/deployment/index>` for some
 popular alternatives. Also, the `server arrangements wiki page`_ contains
 details for several deployment strategies.

+ 8 - 9
docs/howto/deployment/fastcgi.txt

@@ -4,10 +4,9 @@ How to use Django with FastCGI, SCGI, or AJP
 
 .. highlight:: bash
 
-Although the current preferred setup for running Django is :doc:`Apache with
-mod_wsgi </howto/deployment/modwsgi>`, many people use shared hosting, on
-which protocols such as FastCGI, SCGI or AJP are the only viable options. In
-some setups, these protocols may provide better performance than mod_wsgi_.
+Although :doc:`WSGI</howto/deployment/wsgi/index>` is the preferred deployment
+platform for Django, many people use shared hosting, on which protocols such as
+FastCGI, SCGI or AJP are the only viable options.
 
 .. admonition:: Note
 
@@ -20,13 +19,13 @@ serve pages to a Web server. The Web server delegates the incoming Web requests
 (via a socket) to FastCGI, which executes the code and passes the response back
 to the Web server, which, in turn, passes it back to the client's Web browser.
 
-Like mod_wsgi, FastCGI allows code to stay in memory, allowing requests to be
-served with no startup time. While mod_wsgi can either be configured embedded
-in the Apache Web server process or as a separate daemon process, a FastCGI
-process never runs inside the Web server process, always in a separate,
+Like WSGI, FastCGI allows code to stay in memory, allowing requests to be
+served with no startup time. While
+e.g. :doc:`mod_wsgi</howto/deployment/wsgi/modwsgi>` can either be configured
+embedded in the Apache Web server process or as a separate daemon process, a
+FastCGI process never runs inside the Web server process, always in a separate,
 persistent process.
 
-.. _mod_wsgi: http://code.google.com/p/modwsgi/
 .. _mod_perl: http://perl.apache.org/
 
 .. admonition:: Why run code in a separate process?

+ 2 - 3
docs/howto/deployment/index.txt

@@ -9,13 +9,12 @@ ways to easily deploy Django:
 .. toctree::
    :maxdepth: 1
 
-   modwsgi
-   uwsgi
+   wsgi/index
    fastcgi
    mod_python (deprecated) <modpython>
 
 If you're new to deploying Django and/or Python, we'd recommend you try
-:doc:`mod_wsgi </howto/deployment/modwsgi>` first. In most cases it'll be
+:doc:`mod_wsgi </howto/deployment/wsgi/modwsgi>` first. In most cases it'll be
 the easiest, fastest, and most stable deployment choice.
 
 .. seealso::

+ 3 - 3
docs/howto/deployment/modpython.txt

@@ -7,14 +7,14 @@ How to use Django with Apache and mod_python
     Support for mod_python has been deprecated, and will be removed in
     Django 1.5. If you are configuring a new deployment, you are
     strongly encouraged to consider using :doc:`mod_wsgi
-    </howto/deployment/modwsgi>` or any of the other :doc:`supported
-    backends </howto/deployment/index>`.
+    </howto/deployment/wsgi/modwsgi>` or any of the other :doc:`supported
+    servers </howto/deployment/index>`.
 
 .. highlight:: apache
 
 The `mod_python`_ module for Apache_ can be used to deploy Django to a
 production server, although it has been mostly superseded by the simpler
-:doc:`mod_wsgi deployment option </howto/deployment/modwsgi>`.
+:doc:`mod_wsgi deployment option </howto/deployment/wsgi/modwsgi>`.
 
 mod_python is similar to (and inspired by) `mod_perl`_ : It embeds Python within
 Apache and loads Python code into memory when the server starts. Code stays in

+ 0 - 147
docs/howto/deployment/modwsgi.txt

@@ -1,147 +0,0 @@
-==========================================
-How to use Django with Apache and mod_wsgi
-==========================================
-
-Deploying Django with Apache_ and `mod_wsgi`_ is the recommended way to get
-Django into production.
-
-.. _Apache: http://httpd.apache.org/
-.. _mod_wsgi: http://code.google.com/p/modwsgi/
-
-mod_wsgi is an Apache module which can be used to host any Python application
-which supports the Python WSGI interface described in :pep:`3333`, including
-Django. Django will work with any version of Apache which supports mod_wsgi.
-
-The `official mod_wsgi documentation`_ is fantastic; it's your source for all
-the details about how to use mod_wsgi. You'll probably want to start with the
-`installation and configuration documentation`_.
-
-.. _official mod_wsgi documentation: http://code.google.com/p/modwsgi/
-.. _installation and configuration documentation: http://code.google.com/p/modwsgi/wiki/InstallationInstructions
-
-Basic configuration
-===================
-
-Once you've got mod_wsgi installed and activated, edit your ``httpd.conf`` file
-and add::
-
-    WSGIScriptAlias / /path/to/mysite/apache/django.wsgi
-
-The first bit above is the url you want to be serving your application at (``/``
-indicates the root url), and the second is the location of a "WSGI file" -- see
-below -- on your system, usually inside of your project. This tells Apache
-to serve any request below the given URL using the WSGI application defined by that file.
-
-Next we'll need to actually create this WSGI application, so create the file
-mentioned in the second part of ``WSGIScriptAlias`` and add::
-
-    import os
-    import sys
-
-    os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings'
-
-    import django.core.handlers.wsgi
-    application = django.core.handlers.wsgi.WSGIHandler()
-
-If your project is not on your ``PYTHONPATH`` by default you can add::
-
-    path = '/path/to/mysite'
-    if path not in sys.path:
-        sys.path.append(path)
-
-just below the ``import sys`` line to place your project on the path. Remember to
-replace 'mysite.settings' with your correct settings file, and '/path/to/mysite'
-with your own project's location.
-
-.. _serving-files:
-
-Serving files
-=============
-
-Django doesn't serve files itself; it leaves that job to whichever Web
-server you choose.
-
-We recommend using a separate Web server -- i.e., one that's not also running
-Django -- for serving media. Here are some good choices:
-
-* lighttpd_
-* Nginx_
-* TUX_
-* A stripped-down version of Apache_
-* Cherokee_
-
-If, however, you have no option but to serve media files on the same Apache
-``VirtualHost`` as Django, you can set up Apache to serve some URLs as
-static media, and others using the mod_wsgi interface to Django.
-
-This example sets up Django at the site root, but explicitly serves
-``robots.txt``, ``favicon.ico``, any CSS file, and anything in the
-``/static/`` and ``/media/`` URL space as a static file. All other URLs
-will be served using mod_wsgi::
-
-    Alias /robots.txt /usr/local/wsgi/static/robots.txt
-    Alias /favicon.ico /usr/local/wsgi/static/favicon.ico
-
-    AliasMatch ^/([^/]*\.css) /usr/local/wsgi/static/styles/$1
-
-    Alias /media/ /usr/local/wsgi/media/
-    Alias /static/ /usr/local/wsgi/static/
-
-    <Directory /usr/local/wsgi/static>
-    Order deny,allow
-    Allow from all
-    </Directory>
-
-    <Directory /usr/local/wsgi/media>
-    Order deny,allow
-    Allow from all
-    </Directory>
-
-    WSGIScriptAlias / /usr/local/wsgi/scripts/django.wsgi
-
-    <Directory /usr/local/wsgi/scripts>
-    Order allow,deny
-    Allow from all
-    </Directory>
-
-.. _lighttpd: http://www.lighttpd.net/
-.. _Nginx: http://wiki.nginx.org/Main
-.. _TUX: http://en.wikipedia.org/wiki/TUX_web_server
-.. _Apache: http://httpd.apache.org/
-.. _Cherokee: http://www.cherokee-project.com/
-
-.. More details on configuring a mod_wsgi site to serve static files can be found
-.. in the mod_wsgi documentation on `hosting static files`_.
-
-.. _hosting static files: http://code.google.com/p/modwsgi/wiki/ConfigurationGuidelines#Hosting_Of_Static_Files
-
-.. _serving-the-admin-files:
-
-Serving the admin files
-=======================
-
-Note that the Django development server automagically serves the static files
-of the admin app, but this is not the case when you use any other server
-arrangement. You're responsible for setting up Apache, or whichever media
-server you're using, to serve the admin files.
-
-The admin files live in (:file:`django/contrib/admin/static/admin`) of the
-Django distribution.
-
-We **strongly** recommend using :mod:`django.contrib.staticfiles` to handle
-the admin files, but here are two other approaches:
-
-1. Create a symbolic link to the admin static files from within your
-   document root.
-
-2. Or, copy the admin static files so that they live within your Apache
-   document root.
-
-Details
-=======
-
-For more details, see the `mod_wsgi documentation on Django integration`_,
-which explains the above in more detail, and walks through all the various
-options you've got when deploying under mod_wsgi.
-
-.. _mod_wsgi documentation on Django integration: http://code.google.com/p/modwsgi/wiki/IntegrationWithDjango

+ 66 - 0
docs/howto/deployment/wsgi/gunicorn.txt

@@ -0,0 +1,66 @@
+===============================
+How to use Django with Gunicorn
+===============================
+
+.. highlight:: bash
+
+Gunicorn_ ('Green Unicorn') is a pure-Python WSGI server for UNIX. It has no
+dependencies and is easy to install and use.
+
+.. _Gunicorn: http://gunicorn.org/
+
+There are two ways to use Gunicorn with Django. One is to have Gunicorn treat
+Django as just another WSGI application. The second is to use Gunicorn's
+special `integration with Django`_.
+
+.. _integration with Django: http://gunicorn.org/run.html#django-manage-py_
+
+Installing Gunicorn
+===================
+
+Installing gunicorn is as easy as ``pip install gunicorn``. For more details,
+see the `gunicorn documentation`_.
+
+.. _gunicorn documentation: http://gunicorn.org/install.html
+
+Running Django in Gunicorn as a generic WSGI application
+========================================================
+
+When Gunicorn is installed, a ``gunicorn`` command is available which starts
+the Gunicorn server process. At its simplest, gunicorn just needs to be called
+with a the location of a WSGI application object.::
+
+    gunicorn [OPTIONS] APP_MODULE
+
+Where ``APP_MODULE`` is of the pattern ``MODULE_NAME:VARIABLE_NAME``. The
+module name should be a full dotted path. The variable name refers to a WSGI
+callable that should be found in the specified module.
+
+So for a typical Django project, invoking gunicorn would look like::
+
+    gunicorn myproject.wsgi:application
+
+(This requires that your project be on the Python path; the simplest way to
+ensure that is to run this command from the same directory as your
+``manage.py`` file.)
+
+
+Using Gunicorn's Django integration
+===================================
+
+To use Gunicorn's built-in Django integration, first add ``"gunicorn"`` to
+:setting:`INSTALLED_APPS`. Then run ``python manage.py run_gunicorn``.
+
+This provides a few Django-specific niceties:
+
+* sets the gunicorn process name to be that of the project
+
+* validates installed models
+
+* allows an ``--adminmedia`` option for passing in the location of the
+  admin media files, mimicing the behavior of runserver.
+
+See Gunicorn's `deployment documentation`_ for additional tips on starting and
+maintaining the Gunicorn server.
+
+.. _deployment documentation: http://gunicorn.org/deploy.html

+ 72 - 0
docs/howto/deployment/wsgi/index.txt

@@ -0,0 +1,72 @@
+=======================
+How to deploy with WSGI
+=======================
+
+Django's primary deployment platform is WSGI_, the Python standard for web
+servers and applications.
+
+.. _WSGI: http://www.wsgi.org
+
+Django's :djadmin:`startproject` management command sets up a simple default
+WSGI configuration for you, which you can tweak as needed for your project, and
+direct any WSGI-compliant webserver to use. Django includes getting-started
+documentation for the following WSGI servers:
+
+.. toctree::
+   :maxdepth: 1
+
+   modwsgi
+   gunicorn
+   uwsgi
+
+The ``application`` object
+--------------------------
+
+One key concept of deploying with WSGI is to specify a central ``application``
+callable object which the webserver uses to communicate with your code. This is
+commonly specified as an object named ``application`` in a Python module
+accessible to the server.
+
+.. versionchanged:: 1.4
+
+The :djadmin:`startproject` command creates a :file:`projectname/wsgi.py` that
+contains such an application callable.
+
+.. note::
+
+   Upgrading from a previous release of Django and don't have a :file:`wsgi.py`
+   file in your project? You can simply add one to your project's top-level
+   Python package (probably next to :file:`settings.py` and :file:`urls.py`)
+   with the contents below. If you want :djadmin:`runserver` to also make use
+   of this WSGI file, you can also add ``WSGI_APPLICATION =
+   "mysite.wsgi.application"`` in your settings (replacing ``mysite`` with the
+   name of your project).
+
+Initially this file contains::
+
+    import os
+
+    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")
+
+    # This application object is used by the development server
+    # as well as any WSGI server configured to use this file.
+    from django.core.handlers.wsgi import get_wsgi_application
+    application = get_wsgi_application()
+
+The ``os.environ.setdefault`` line just sets the default settings module to
+use, if you haven't explicitly set the :envvar:`DJANGO_SETTINGS_MODULE`
+environment variable. You'll need to edit this line to replace ``mysite`` with
+the name of your project package, so the path to your settings module is
+correct.
+
+To apply `WSGI middleware`_ you can simply wrap the application object
+in the same file::
+
+    from helloworld.wsgi import HelloWorldApplication
+    application = HelloWorldApplication(application)
+
+You could also replace the Django WSGI application with a custom WSGI
+application that later delegates to the Django WSGI application, if you want to
+combine a Django application with a WSGI application of another framework.
+
+.. _`WSGI middleware`: http://www.python.org/dev/peps/pep-3333/#middleware-components-that-play-both-sides

+ 175 - 0
docs/howto/deployment/wsgi/modwsgi.txt

@@ -0,0 +1,175 @@
+==========================================
+How to use Django with Apache and mod_wsgi
+==========================================
+
+Deploying Django with Apache_ and `mod_wsgi`_ is a tried and tested way to get
+Django into production.
+
+.. _Apache: http://httpd.apache.org/
+.. _mod_wsgi: http://code.google.com/p/modwsgi/
+
+mod_wsgi is an Apache module which can host any Python WSGI_ application,
+including Django. Django will work with any version of Apache which supports
+mod_wsgi.
+
+.. _WSGI: http://www.wsgi.org
+
+The `official mod_wsgi documentation`_ is fantastic; it's your source for all
+the details about how to use mod_wsgi. You'll probably want to start with the
+`installation and configuration documentation`_.
+
+.. _official mod_wsgi documentation: http://www.modwsgi.org/
+.. _installation and configuration documentation: http://www.modwsgi.org/wiki/InstallationInstructions
+
+Basic configuration
+===================
+
+Once you've got mod_wsgi installed and activated, edit your Apache server's
+``httpd.conf`` file and add::
+
+    WSGIScriptAlias / /path/to/mysite.com/mysite/wsgi.py
+    WSGIPythonPath /path/to/mysite.com
+
+    <Directory /path/to/mysite.com/mysite>
+    <Files wsgi.py>
+    Order deny,allow
+    Allow from all
+    </Files>
+    </Directory>
+
+The first bit in the ``WSGIScriptAlias`` line is the base URL path you want to
+serve your application at (``/`` indicates the root url), and the second is the
+location of a "WSGI file" -- see below -- on your system, usually inside of
+your project package (``mysite`` in this example). This tells Apache to serve
+any request below the given URL using the WSGI application defined in that
+file.
+
+The ``WSGIPythonPath`` line ensures that your project package is available for
+import on the Python path; in other words, that ``import mysite`` works.
+
+The ``<Directory>`` piece just ensures that Apache can access your
+:file:`wsgi.py` file.
+
+Next we'll need to ensure this :file:`wsgi.py` with a WSGI application object
+exists. As of Django version 1.4, :djadmin:`startproject` will have created one
+for you; otherwise, you'll need to create it. See the :doc:`WSGI overview
+documentation</howto/deployment/wsgi/index>` for the default contents you
+should put in this file, and what else you can add to it.
+
+Using a virtualenv
+==================
+
+If you install your project's Python dependencies inside a `virtualenv`_,
+you'll need to add the path to this virtualenv's ``site-packages`` directory to
+your Python path as well. To do this, you can add another line to your
+Apache configuration::
+
+    WSGIPythonPath /path/to/your/venv/lib/python2.X/site-packages
+
+Make sure you give the correct path to your virtualenv, and replace
+``python2.X`` with the correct Python version (e.g. ``python2.7``).
+
+.. _virtualenv: http://www.virtualenv.org
+
+Using mod_wsgi daemon mode
+==========================
+
+"Daemon mode" is the recommended mode for running mod_wsgi (on non-Windows
+platforms). See the `official mod_wsgi documentation`_ for details on setting
+up daemon mode. The only change required to the above configuration if you use
+daemon mode is that you can't use ``WSGIPythonPath``; instead you should use
+the ``python-path`` option to ``WSGIDaemonProcess``, for example::
+
+    WSGIDaemonProcess example.com python-path=/path/to/mysite.com:/path/to/venv/lib/python2.7/site-packages
+
+.. _serving-files:
+
+Serving files
+=============
+
+Django doesn't serve files itself; it leaves that job to whichever Web
+server you choose.
+
+We recommend using a separate Web server -- i.e., one that's not also running
+Django -- for serving media. Here are some good choices:
+
+* lighttpd_
+* Nginx_
+* TUX_
+* A stripped-down version of Apache_
+* Cherokee_
+
+If, however, you have no option but to serve media files on the same Apache
+``VirtualHost`` as Django, you can set up Apache to serve some URLs as
+static media, and others using the mod_wsgi interface to Django.
+
+This example sets up Django at the site root, but explicitly serves
+``robots.txt``, ``favicon.ico``, any CSS file, and anything in the
+``/static/`` and ``/media/`` URL space as a static file. All other URLs
+will be served using mod_wsgi::
+
+    Alias /robots.txt /path/to/mysite.com/static/robots.txt
+    Alias /favicon.ico /path/to/mysite.com/static/favicon.ico
+
+    AliasMatch ^/([^/]*\.css) /path/to/mysite.com/static/styles/$1
+
+    Alias /media/ /path/to/mysite.com/media/
+    Alias /static/ /path/to/mysite.com/static/
+
+    <Directory /path/to/mysite.com/static>
+    Order deny,allow
+    Allow from all
+    </Directory>
+
+    <Directory /path/to/mysite.com/media>
+    Order deny,allow
+    Allow from all
+    </Directory>
+
+    WSGIScriptAlias / /path/to/mysite.com/mysite/wsgi.py
+
+    <Directory /path/to/mysite.com/mysite>
+    <Files wsgi.py>
+    Order allow,deny
+    Allow from all
+    </Files>
+    </Directory>
+
+.. _lighttpd: http://www.lighttpd.net/
+.. _Nginx: http://wiki.nginx.org/Main
+.. _TUX: http://en.wikipedia.org/wiki/TUX_web_server
+.. _Apache: http://httpd.apache.org/
+.. _Cherokee: http://www.cherokee-project.com/
+
+.. More details on configuring a mod_wsgi site to serve static files can be found
+.. in the mod_wsgi documentation on `hosting static files`_.
+
+.. _hosting static files: http://code.google.com/p/modwsgi/wiki/ConfigurationGuidelines#Hosting_Of_Static_Files
+
+.. _serving-the-admin-files:
+
+Serving the admin files
+=======================
+
+Note that the Django development server automatically serves the static files
+of the admin app (and any other installed apps), but this is not the case when
+you use any other server arrangement. You're responsible for setting up Apache,
+or whichever media server you're using, to serve the admin files.
+
+The admin files live in (:file:`django/contrib/admin/static/admin`) of the
+Django distribution.
+
+We **strongly** recommend using :mod:`django.contrib.staticfiles` (along with
+a Web server as outlined in the previous section) to handle the admin files, but
+here are three other approaches:
+
+1. Create a symbolic link to the admin static files from within your
+   document root (this may require ``+FollowSymLinks`` in your Apache
+   configuration).
+
+2. Use an ``Alias`` directive, as demonstrated above, to alias the appropriate
+   URL (probably :setting:`STATIC_URL` + `admin/`) to the actual location of
+   the admin files.
+
+3. Copy the admin static files so that they live within your Apache
+   document root.

+ 31 - 21
docs/howto/deployment/uwsgi.txt → docs/howto/deployment/wsgi/uwsgi.txt

@@ -36,7 +36,7 @@ uWSGI model
 -----------
 
 uWSGI operates on a client-server model. Your Web server (ie. nginx, Apache)
-communicates with a django-uwsgi "worker" process to serve dynamic contents.
+communicates with a django-uwsgi "worker" process to serve dynamic content.
 The Web server can communicate with the uWSGI process either:
 
 * directly by the uWSGI protocol through a socket created by uWSGI,
@@ -48,9 +48,9 @@ systems), or it can use a TCP socket. What you choose is a matterr of
 preference. Usually, a TCP socket is easier because connecting to a port
 doesn't require special permissions.
 
-In the second case, the Web server doesn't need to do uWSGI protocol. It just
-needs to be able to proxy HTTP requests to the HTTP server built-in uWSGI.
-The procedure is the same than proxying any HTTP server. Note that the Web
+In the second case, the Web server doesn't need to speak the uWSGI protocol. It
+just needs to be able to proxy HTTP requests to the HTTP server built-in uWSGI.
+The procedure is the same as proxying to any HTTP server. Note that the Web
 server is a "reverse proxy" in this case.
 
 Configuring the uWSGI server
@@ -68,9 +68,9 @@ the uWSGI server.
     on that file.
 
 uWSGI is highly configurable and thus there are many ways to start the
-process. For example, uwsgi version 0.9.6.8 provides a hundred switches.
-This guide demonstrates the most important of them, but does not intent to
-substitute the official manual and online documentation.
+process. For example, uwsgi version 0.9.6.8 provides a hundred switches.  This
+guide demonstrates the most important of them, but is not a substitute the
+official manual and online documentation.
 
 uWSGI supports configuration through:
 
@@ -98,8 +98,8 @@ uWSGI server. This means:
 
 * the uWSGI server can be restarted or reloaded independently from the Web
   server,
-* (except with Cheerokee), it is the role of the system administrator to make
-  uWSGI to start on boot or reboot: either through tools like supervisor or
+* (except with Cherokee), it is the role of the system administrator to make
+  uWSGI start on boot or reboot: either through tools like supervisor or
   daemontools, either directly at init level in a file like /etc/rc.local or
   /etc/conf.d/local
 
@@ -109,11 +109,11 @@ Managing uWSGI
 Starting the server
 -------------------
 
-Example command line for a Web server that understand the uWSGI protocol::
+Example command line for a Web server that understands the uWSGI protocol::
 
     uwsgi --chdir=/path/to/your/project
-        --module='django.core.handlers.wsgi:WSGIHandler()' \
-        --env DJANGO_SETTINGS_MODULE=settings \
+        --module='mysite.wsgi:application' \
+        --env DJANGO_SETTINGS_MODULE=mysite.settings \
         --master --pidfile=/tmp/project-master.pid \
         --socket=127.0.0.1:49152 \      # can also be a file
         --processes=5 \                 # number of worker processes
@@ -125,17 +125,27 @@ Example command line for a Web server that understand the uWSGI protocol::
         --home=/path/to/virtual/env \   # optionnal path to a virtualenv
         --daemonize=/var/log/uwsgi/yourproject.log      # background the process
 
-Django specific options are:
+This assumes that you have a top-level project package named ``mysite``, and
+within it a module :file:`mysite/wsgi.py` that contains a WSGI ``application``
+object. This is the layout you will have if you ran ``django-admin.py
+startproject mysite`` (using your own project name in place of ``mysite``) with
+a recent version of Django. If this file does not exist, you'll need to create
+it. See the :doc:`/howto/deployment/wsgi/index` documentation for the default
+contents you should put in this file, and what else you can add to it.
 
-* ``chdir``: should be the path to your project
-* ``module``: uwsgi module to use
-* ``pythonpath``: optional path to your project virtualenv
-* ``env``: should contain at least ``DJANGO_SETTINGS_MODULE``
+The Django-specific options here are:
+
+* ``chdir``: the path to the directory that needs to be on Python's import path; i.e. the directory containing the ``mysite`` package.
+* ``module``: The WSGI module to use, probably the ``mysite.wsgi`` module which
+  :djadmin:`startproject` creates.
+* ``env``: should probably contain at least ``DJANGO_SETTINGS_MODULE``
+* ``home``: optional path to your project virtualenv
 
 Example ini configuration file::
 
     [uwsgi]
     chdir=/path/to/your/project
+    module='mysite.wsgi:application'
     master=True
     pidfile=/tmp/project-master.pid
     vacuum=True
@@ -157,7 +167,7 @@ Read more `uWSGI configuration examples
 Reloading the daemon
 --------------------
 
-As mentioned above, the uWSGI master process is one of the core component of
+As mentioned above, the uWSGI master process is one of the core components of
 the uWSGI stack. The signal to brutally reload all the workers and the master
 process is SIGTERM. Example command to brutally reload the uWSGI processes::
 
@@ -167,7 +177,7 @@ Patching the daemon
 -------------------
 
 One of the great advantages of uWSGI is its ability to gradually restart each
-worker without loosing any request.
+worker without losing any requests.
 
 For example, uWSGI can be signaled that worker should reload the code after
 handling their current request (if any) from bash::
@@ -236,7 +246,7 @@ still experimental.
 Troubleshooting
 ===============
 
-As usual, the first things to do is to check the logs. This implies:
+As usual, the first thing to do is to check the logs. This implies:
 
 * the web server log, which will indicate if it couldn't connect to the uWSGI
   process,
@@ -251,5 +261,5 @@ Typical gotchas:
   killed with ``SIGKILL``, it won't remove the socket and pidfile when it is
   interrupted. It is safe to remove them manually and to start uWSGI again in
   that case.
-* uWSGI can start the process on the foreground, this will make errors easily
+* uWSGI can start the process in the foreground, this will make errors easily
   visible to the system administrator.

+ 2 - 3
docs/index.txt

@@ -154,10 +154,9 @@ The development process
 
 * **Deployment:**
   :doc:`Overview <howto/deployment/index>` |
-  :doc:`Apache/mod_wsgi <howto/deployment/modwsgi>` |
-  :doc:`uWSGI <howto/deployment/uwsgi>` |
-  :doc:`Apache/mod_python (deprecated) <howto/deployment/modpython>` |
+  :doc:`WSGI servers <howto/deployment/wsgi/index>` |
   :doc:`FastCGI/SCGI/AJP <howto/deployment/fastcgi>` |
+  :doc:`Apache/mod_python (deprecated) <howto/deployment/modpython>` |
   :doc:`Apache authentication <howto/apache-auth>` |
   :doc:`Handling static files <howto/static-files>` |
   :doc:`Tracking code errors by email <howto/error-reporting>`

+ 4 - 0
docs/intro/tutorial01.txt

@@ -95,6 +95,7 @@ Let's look at what :djadmin:`startproject` created::
             __init__.py
             settings.py
             urls.py
+            wsgi.py
 
 .. admonition:: Doesn't match what you see?
 
@@ -129,6 +130,9 @@ These files are:
   "table of contents" of your Django-powered site. You can read more about
   URLs in :doc:`/topics/http/urls`.
 
+* :file:`mysite/wsgi.py`: An entry-point for WSGI-compatible webservers to
+  serve your project. See :doc:`/howto/deployment/wsgi/index` for more details.
+
 .. _more about packages: http://docs.python.org/tutorial/modules.html#packages
 
 The development server

+ 3 - 3
docs/ref/contrib/gis/deployment.txt

@@ -54,7 +54,7 @@ Example::
     number of ``processes`` instead.
 
 For more information, please consult Django's
-:doc:`mod_wsgi documentation </howto/deployment/modwsgi>`.
+:doc:`mod_wsgi documentation </howto/deployment/wsgi/modwsgi>`.
 
 ``mod_python``
 --------------
@@ -62,8 +62,8 @@ For more information, please consult Django's
 .. warning::
     Support for mod_python will be deprecated in a future release of Django. If
     you are configuring a new deployment, you are strongly encouraged to
-    consider using :doc:`mod_wsgi </howto/deployment/modwsgi>` or any of the
-    other :doc:`supported backends </howto/deployment/index>`.
+    consider using :doc:`mod_wsgi </howto/deployment/wsgi/modwsgi>` or any of
+    the other :doc:`supported servers </howto/deployment/index>`.
 
 Example::
 

+ 8 - 0
docs/ref/django-admin.txt

@@ -504,6 +504,10 @@ supports the FastCGI protocol. See the :doc:`FastCGI deployment documentation
 </howto/deployment/fastcgi>` for details. Requires the Python FastCGI module from
 `flup`_.
 
+.. versionadded:: 1.4
+    Internally, this wraps the WSGI application object specified by the
+    :setting:`WSGI_APPLICATION` setting.
+
 .. _flup: http://www.saddi.com/software/flup/
 
 The options accepted by this command are passed to the FastCGI library and
@@ -628,6 +632,10 @@ If you run this script as a user with normal privileges (recommended), you
 might not have access to start a port on a low port number. Low port numbers
 are reserved for the superuser (root).
 
+.. versionadded:: 1.4
+    This server uses the WSGI application object specified by the
+    :setting:`WSGI_APPLICATION` setting.
+
 DO NOT USE THIS SERVER IN A PRODUCTION SETTING. It has not gone through
 security audits or performance tests. (And that's how it's gonna stay. We're in
 the business of making Web frameworks, not Web servers, so improving this

+ 19 - 0
docs/ref/settings.txt

@@ -2095,6 +2095,25 @@ A boolean that specifies whether to use the X-Forwarded-Host header in
 preference to the Host header. This should only be enabled if a proxy
 which sets this header is in use.
 
+.. setting:: WSGI_APPLICATION
+
+WSGI_APPLICATION
+----------------
+
+.. versionadded:: 1.4
+
+Default: ``None``
+
+The full Python path of the WSGI application object that Django's built-in
+servers (e.g. :djadmin:`runserver`) will use. The :djadmin:`django-admin.py
+startproject <startproject>` management command will create a simple
+``wsgi.py`` file with an ``application`` callable in it, and point this setting
+to that ``application``.
+
+If not set, the return value of :func:`django.core.wsgi.get_wsgi_application`
+will be used. In this case, the behavior of :djadmin:`runserver` will be
+identical to previous Django versions.
+
 .. setting:: YEAR_MONTH_FORMAT
 
 YEAR_MONTH_FORMAT

+ 1 - 1
docs/releases/1.3-alpha-1.txt

@@ -303,7 +303,7 @@ more flexible ``mod_wsgi`` backend.
 
 If you are currently using the ``mod_python`` request handler, you are strongly
 encouraged to redeploy your Django instances using :doc:`mod_wsgi
-</howto/deployment/modwsgi>`.
+</howto/deployment/wsgi/modwsgi>`.
 
 Function-based generic views
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+ 1 - 1
docs/releases/1.3.txt

@@ -688,7 +688,7 @@ more flexible ``mod_wsgi`` backend.
 
 If you are currently using the ``mod_python`` request handler, you
 should redeploy your Django projects using another request handler.
-:doc:`mod_wsgi </howto/deployment/modwsgi>` is the request handler
+:doc:`mod_wsgi </howto/deployment/wsgi/modwsgi>` is the request handler
 recommended by the Django project, but :doc:`FastCGI
 </howto/deployment/fastcgi>` is also supported. Support for
 ``mod_python`` deployment will be removed in Django 1.5.

+ 18 - 0
docs/releases/1.4.txt

@@ -403,6 +403,24 @@ prefix, some places without it), the imports will need to be cleaned up when
 switching to the new ``manage.py``.
 
 
+Improved WSGI support
+~~~~~~~~~~~~~~~~~~~~~
+
+The :djadmin:`startproject` management command now adds a :file:`wsgi.py`
+module to the initial project layout, containing a simple WSGI application that
+can be used for :doc:`deploying with WSGI app
+servers</howto/deployment/wsgi/index>`.
+
+The :djadmin:`built-in development server<runserver>` now supports using an
+externally-defined WSGI callable, so as to make it possible to run runserver
+with the same WSGI configuration that is used for deployment. A new
+:setting:`WSGI_APPLICATION` setting is available to configure which WSGI
+callable :djadmin:`runserver` uses.
+
+(The :djadmin:`runfcgi` management command also internally wraps the WSGI
+callable configured via :setting:`WSGI_APPLICATION`.)
+
+
 Minor features
 ~~~~~~~~~~~~~~
 

+ 1 - 1
docs/topics/install.txt

@@ -48,7 +48,7 @@ documentation to determine which mode is right for your setup. Make
 sure you have Apache installed, with the mod_wsgi module activated.
 Django will work with any version of Apache that supports mod_wsgi.
 
-See :doc:`How to use Django with mod_wsgi </howto/deployment/modwsgi>`
+See :doc:`How to use Django with mod_wsgi </howto/deployment/wsgi/modwsgi>`
 for information on how to configure mod_wsgi once you have it
 installed.
 

+ 1 - 1
docs/topics/settings.txt

@@ -75,7 +75,7 @@ application what settings file to use. Do that with ``os.environ``::
     os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings'
 
 Read the :doc:`Django mod_wsgi documentation
-</howto/deployment/modwsgi>` for more information and other common
+</howto/deployment/wsgi/modwsgi>` for more information and other common
 elements to a Django WSGI application.
 
 Default settings

+ 0 - 0
tests/regressiontests/wsgi/__init__.py


+ 0 - 0
tests/regressiontests/wsgi/models.py


+ 102 - 0
tests/regressiontests/wsgi/tests.py

@@ -0,0 +1,102 @@
+from __future__ import with_statement
+
+from django.core.exceptions import ImproperlyConfigured
+from django.core.servers.basehttp import get_internal_wsgi_application
+from django.core.wsgi import get_wsgi_application
+from django.test import TestCase
+from django.test.client import RequestFactory
+from django.test.utils import override_settings
+from django.utils import unittest
+
+
+class WSGITest(TestCase):
+    urls = "regressiontests.wsgi.urls"
+
+    def test_get_wsgi_application(self):
+        """
+        Verify that ``get_wsgi_application`` returns a functioning WSGI
+        callable.
+
+        """
+        application = get_wsgi_application()
+
+        environ = RequestFactory()._base_environ(
+            PATH_INFO="/",
+            CONTENT_TYPE="text/html; charset=utf-8",
+            REQUEST_METHOD="GET"
+            )
+
+        response_data = {}
+
+        def start_response(status, headers):
+            response_data["status"] = status
+            response_data["headers"] = headers
+
+        response = application(environ, start_response)
+
+        self.assertEqual(response_data["status"], "200 OK")
+        self.assertEqual(
+            response_data["headers"],
+            [('Content-Type', 'text/html; charset=utf-8')])
+        self.assertEqual(
+            unicode(response),
+            u"Content-Type: text/html; charset=utf-8\n\nHello World!")
+
+
+class GetInternalWSGIApplicationTest(unittest.TestCase):
+    @override_settings(WSGI_APPLICATION="regressiontests.wsgi.wsgi.application")
+    def test_success(self):
+        """
+        If ``WSGI_APPLICATION`` is a dotted path, the referenced object is
+        returned.
+
+        """
+        app = get_internal_wsgi_application()
+
+        from .wsgi import application
+
+        self.assertTrue(app is application)
+
+
+    @override_settings(WSGI_APPLICATION=None)
+    def test_default(self):
+        """
+        If ``WSGI_APPLICATION`` is ``None``, the return value of
+        ``get_wsgi_application`` is returned.
+
+        """
+        # Mock out get_wsgi_application so we know its return value is used
+        fake_app = object()
+        def mock_get_wsgi_app():
+            return fake_app
+        from django.core.servers import basehttp
+        _orig_get_wsgi_app = basehttp.get_wsgi_application
+        basehttp.get_wsgi_application = mock_get_wsgi_app
+
+        try:
+            app = get_internal_wsgi_application()
+
+            self.assertTrue(app is fake_app)
+        finally:
+            basehttp.get_wsgi_application = _orig_get_wsgi_app
+
+
+    @override_settings(WSGI_APPLICATION="regressiontests.wsgi.noexist.app")
+    def test_bad_module(self):
+        with self.assertRaises(ImproperlyConfigured) as cm:
+            get_internal_wsgi_application()
+
+        self.assertEqual(
+            str(cm.exception),
+            "WSGI application 'regressiontests.wsgi.noexist.app' could not be loaded; could not import module 'regressiontests.wsgi.noexist': No module named noexist")
+
+
+    @override_settings(WSGI_APPLICATION="regressiontests.wsgi.wsgi.noexist")
+    def test_bad_name(self):
+        with self.assertRaises(ImproperlyConfigured) as cm:
+            get_internal_wsgi_application()
+
+        self.assertEqual(
+            str(cm.exception),
+            "WSGI application 'regressiontests.wsgi.wsgi.noexist' could not be loaded; can't find 'noexist' in module 'regressiontests.wsgi.wsgi': 'module' object has no attribute 'noexist'")
+

+ 10 - 0
tests/regressiontests/wsgi/urls.py

@@ -0,0 +1,10 @@
+from django.conf.urls import url, patterns
+from django.http import HttpResponse
+
+def helloworld(request):
+    return HttpResponse("Hello World!")
+
+urlpatterns = patterns(
+    "",
+    url("^$", helloworld)
+    )

+ 2 - 0
tests/regressiontests/wsgi/wsgi.py

@@ -0,0 +1,2 @@
+# This is just to test finding, it doesn't have to be a real WSGI callable
+application = object()