diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py index 766d0d6010..6b09be23b2 100644 --- a/django/conf/global_settings.py +++ b/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 # ############## diff --git a/django/conf/project_template/project_name/settings.py b/django/conf/project_template/project_name/settings.py index b92c1163c3..aaaeb37688 100644 --- a/django/conf/project_template/project_name/settings.py +++ b/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. diff --git a/django/conf/project_template/project_name/wsgi.py b/django/conf/project_template/project_name/wsgi.py new file mode 100644 index 0000000000..b083a0e699 --- /dev/null +++ b/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) diff --git a/django/contrib/staticfiles/management/commands/runserver.py b/django/contrib/staticfiles/management/commands/runserver.py index c6e56d2b98..403df88d81 100644 --- a/django/contrib/staticfiles/management/commands/runserver.py +++ b/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 diff --git a/django/core/handlers/base.py b/django/core/handlers/base.py index b7c0ff8c5c..3606e1853c 100644 --- a/django/core/handlers/base.py +++ b/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: diff --git a/django/core/handlers/wsgi.py b/django/core/handlers/wsgi.py index a816cadc7b..d6e674b6de 100644 --- a/django/core/handlers/wsgi.py +++ b/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 - diff --git a/django/core/management/commands/runserver.py b/django/core/management/commands/runserver.py index 2e693e7e49..4191b55dcb 100644 --- a/django/core/management/commands/runserver.py +++ b/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') diff --git a/django/core/servers/basehttp.py b/django/core/servers/basehttp.py index 560637fa5c..8d4ceabfc3 100644 --- a/django/core/servers/basehttp.py +++ b/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 diff --git a/django/core/servers/fastcgi.py b/django/core/servers/fastcgi.py index 9f80d2ba44..cacac3d0ec 100644 --- a/django/core/servers/fastcgi.py +++ b/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:]) diff --git a/django/core/wsgi.py b/django/core/wsgi.py new file mode 100644 index 0000000000..edea3334c9 --- /dev/null +++ b/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() diff --git a/docs/faq/install.txt b/docs/faq/install.txt index 90b83f18ee..434a0814c3 100644 --- a/docs/faq/install.txt +++ b/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 ` for some popular alternatives. Also, the `server arrangements wiki page`_ contains details for several deployment strategies. diff --git a/docs/howto/deployment/fastcgi.txt b/docs/howto/deployment/fastcgi.txt index c2a4086bdd..6a5acfb7cc 100644 --- a/docs/howto/deployment/fastcgi.txt +++ b/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 `, 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` 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` 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? diff --git a/docs/howto/deployment/index.txt b/docs/howto/deployment/index.txt index fdfce3e996..113f6067fb 100644 --- a/docs/howto/deployment/index.txt +++ b/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) If you're new to deploying Django and/or Python, we'd recommend you try -:doc:`mod_wsgi ` first. In most cases it'll be +:doc:`mod_wsgi ` first. In most cases it'll be the easiest, fastest, and most stable deployment choice. .. seealso:: diff --git a/docs/howto/deployment/modpython.txt b/docs/howto/deployment/modpython.txt index 201f45b72a..952cb3247b 100644 --- a/docs/howto/deployment/modpython.txt +++ b/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 - ` or any of the other :doc:`supported - backends `. + ` or any of the other :doc:`supported + servers `. .. 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 `. +:doc:`mod_wsgi deployment option `. 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 diff --git a/docs/howto/deployment/modwsgi.txt b/docs/howto/deployment/modwsgi.txt deleted file mode 100644 index 5f93caed07..0000000000 --- a/docs/howto/deployment/modwsgi.txt +++ /dev/null @@ -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/ - - - Order deny,allow - Allow from all - - - - Order deny,allow - Allow from all - - - WSGIScriptAlias / /usr/local/wsgi/scripts/django.wsgi - - - Order allow,deny - Allow from all - - -.. _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 diff --git a/docs/howto/deployment/wsgi/gunicorn.txt b/docs/howto/deployment/wsgi/gunicorn.txt new file mode 100644 index 0000000000..609f7ccc86 --- /dev/null +++ b/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 diff --git a/docs/howto/deployment/wsgi/index.txt b/docs/howto/deployment/wsgi/index.txt new file mode 100644 index 0000000000..c666aafac0 --- /dev/null +++ b/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 diff --git a/docs/howto/deployment/wsgi/modwsgi.txt b/docs/howto/deployment/wsgi/modwsgi.txt new file mode 100644 index 0000000000..7ef0fb47d5 --- /dev/null +++ b/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 + + + + Order deny,allow + Allow from all + + + +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 ```` 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` 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/ + + + Order deny,allow + Allow from all + + + + Order deny,allow + Allow from all + + + WSGIScriptAlias / /path/to/mysite.com/mysite/wsgi.py + + + + Order allow,deny + Allow from all + + + +.. _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. diff --git a/docs/howto/deployment/uwsgi.txt b/docs/howto/deployment/wsgi/uwsgi.txt similarity index 80% rename from docs/howto/deployment/uwsgi.txt rename to docs/howto/deployment/wsgi/uwsgi.txt index 11cb51c544..976caf82fa 100644 --- a/docs/howto/deployment/uwsgi.txt +++ b/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. diff --git a/docs/index.txt b/docs/index.txt index 1739a2c994..faa0428e63 100644 --- a/docs/index.txt +++ b/docs/index.txt @@ -154,10 +154,9 @@ The development process * **Deployment:** :doc:`Overview ` | - :doc:`Apache/mod_wsgi ` | - :doc:`uWSGI ` | - :doc:`Apache/mod_python (deprecated) ` | + :doc:`WSGI servers ` | :doc:`FastCGI/SCGI/AJP ` | + :doc:`Apache/mod_python (deprecated) ` | :doc:`Apache authentication ` | :doc:`Handling static files ` | :doc:`Tracking code errors by email ` diff --git a/docs/intro/tutorial01.txt b/docs/intro/tutorial01.txt index 40b00413db..8474d9ebb8 100644 --- a/docs/intro/tutorial01.txt +++ b/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 diff --git a/docs/ref/contrib/gis/deployment.txt b/docs/ref/contrib/gis/deployment.txt index 14f803d54f..4cea022815 100644 --- a/docs/ref/contrib/gis/deployment.txt +++ b/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 `. +:doc:`mod_wsgi documentation `. ``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 ` or any of the - other :doc:`supported backends `. + consider using :doc:`mod_wsgi ` or any of + the other :doc:`supported servers `. Example:: diff --git a/docs/ref/django-admin.txt b/docs/ref/django-admin.txt index f397b04a1f..519ef3c766 100644 --- a/docs/ref/django-admin.txt +++ b/docs/ref/django-admin.txt @@ -504,6 +504,10 @@ supports the FastCGI protocol. See the :doc:`FastCGI deployment documentation ` 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 diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index 4ff5d4d068..c3539192f3 100644 --- a/docs/ref/settings.txt +++ b/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 ` 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 diff --git a/docs/releases/1.3-alpha-1.txt b/docs/releases/1.3-alpha-1.txt index b0929d164b..92cc352c9b 100644 --- a/docs/releases/1.3-alpha-1.txt +++ b/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 -`. +`. Function-based generic views ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/releases/1.3.txt b/docs/releases/1.3.txt index 68b846b0f1..8e63c0fe00 100644 --- a/docs/releases/1.3.txt +++ b/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 ` is the request handler +:doc:`mod_wsgi ` is the request handler recommended by the Django project, but :doc:`FastCGI ` is also supported. Support for ``mod_python`` deployment will be removed in Django 1.5. diff --git a/docs/releases/1.4.txt b/docs/releases/1.4.txt index 00acc65922..69cf90b590 100644 --- a/docs/releases/1.4.txt +++ b/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`. + +The :djadmin:`built-in development server` 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 ~~~~~~~~~~~~~~ diff --git a/docs/topics/install.txt b/docs/topics/install.txt index 089e1d3781..fc513af640 100644 --- a/docs/topics/install.txt +++ b/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 ` +See :doc:`How to use Django with mod_wsgi ` for information on how to configure mod_wsgi once you have it installed. diff --git a/docs/topics/settings.txt b/docs/topics/settings.txt index 8e4f1dfe57..6c9d2ece04 100644 --- a/docs/topics/settings.txt +++ b/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 -` for more information and other common +` for more information and other common elements to a Django WSGI application. Default settings diff --git a/tests/regressiontests/wsgi/__init__.py b/tests/regressiontests/wsgi/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regressiontests/wsgi/models.py b/tests/regressiontests/wsgi/models.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regressiontests/wsgi/tests.py b/tests/regressiontests/wsgi/tests.py new file mode 100644 index 0000000000..6b0c433222 --- /dev/null +++ b/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'") + diff --git a/tests/regressiontests/wsgi/urls.py b/tests/regressiontests/wsgi/urls.py new file mode 100644 index 0000000000..9639f0fd34 --- /dev/null +++ b/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) + ) diff --git a/tests/regressiontests/wsgi/wsgi.py b/tests/regressiontests/wsgi/wsgi.py new file mode 100644 index 0000000000..400457f152 --- /dev/null +++ b/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()