diff options
author | Nishanth Amuluru | 2011-01-08 11:20:57 +0530 |
---|---|---|
committer | Nishanth Amuluru | 2011-01-08 11:20:57 +0530 |
commit | 65411d01d448ff0cd4abd14eee14cf60b5f8fc20 (patch) | |
tree | b4c404363c4c63a61d6e2f8bd26c5b057c1fb09d /parts/django/docs/howto | |
parent | 2e35094d43b4cc6974172e1febf76abb50f086ec (diff) | |
download | pytask-65411d01d448ff0cd4abd14eee14cf60b5f8fc20.tar.gz pytask-65411d01d448ff0cd4abd14eee14cf60b5f8fc20.tar.bz2 pytask-65411d01d448ff0cd4abd14eee14cf60b5f8fc20.zip |
Added buildout stuff and made changes accordingly
--HG--
rename : profile/management/__init__.py => eggs/djangorecipe-0.20-py2.6.egg/EGG-INFO/dependency_links.txt
rename : profile/management/__init__.py => eggs/djangorecipe-0.20-py2.6.egg/EGG-INFO/not-zip-safe
rename : profile/management/__init__.py => eggs/infrae.subversion-1.4.5-py2.6.egg/EGG-INFO/dependency_links.txt
rename : profile/management/__init__.py => eggs/infrae.subversion-1.4.5-py2.6.egg/EGG-INFO/not-zip-safe
rename : profile/management/__init__.py => eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/EGG-INFO/dependency_links.txt
rename : profile/management/__init__.py => eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/EGG-INFO/not-zip-safe
rename : profile/management/__init__.py => eggs/py-1.4.0-py2.6.egg/EGG-INFO/dependency_links.txt
rename : profile/management/__init__.py => eggs/py-1.4.0-py2.6.egg/EGG-INFO/not-zip-safe
rename : profile/management/__init__.py => eggs/zc.buildout-1.5.2-py2.6.egg/EGG-INFO/dependency_links.txt
rename : profile/management/__init__.py => eggs/zc.buildout-1.5.2-py2.6.egg/EGG-INFO/not-zip-safe
rename : profile/management/__init__.py => eggs/zc.recipe.egg-1.3.2-py2.6.egg/EGG-INFO/dependency_links.txt
rename : profile/management/__init__.py => eggs/zc.recipe.egg-1.3.2-py2.6.egg/EGG-INFO/not-zip-safe
rename : profile/management/__init__.py => parts/django/Django.egg-info/dependency_links.txt
rename : taskapp/models.py => parts/django/django/conf/app_template/models.py
rename : taskapp/tests.py => parts/django/django/conf/app_template/tests.py
rename : taskapp/views.py => parts/django/django/conf/app_template/views.py
rename : taskapp/views.py => parts/django/django/contrib/gis/tests/geo3d/views.py
rename : profile/management/__init__.py => parts/django/tests/modeltests/delete/__init__.py
rename : profile/management/__init__.py => parts/django/tests/modeltests/files/__init__.py
rename : profile/management/__init__.py => parts/django/tests/modeltests/invalid_models/__init__.py
rename : profile/management/__init__.py => parts/django/tests/modeltests/m2m_signals/__init__.py
rename : profile/management/__init__.py => parts/django/tests/modeltests/model_package/__init__.py
rename : profile/management/__init__.py => parts/django/tests/regressiontests/bash_completion/__init__.py
rename : profile/management/__init__.py => parts/django/tests/regressiontests/bash_completion/management/__init__.py
rename : profile/management/__init__.py => parts/django/tests/regressiontests/bash_completion/management/commands/__init__.py
rename : profile/management/__init__.py => parts/django/tests/regressiontests/bash_completion/models.py
rename : profile/management/__init__.py => parts/django/tests/regressiontests/delete_regress/__init__.py
rename : profile/management/__init__.py => parts/django/tests/regressiontests/file_storage/__init__.py
rename : profile/management/__init__.py => parts/django/tests/regressiontests/max_lengths/__init__.py
rename : profile/forms.py => pytask/profile/forms.py
rename : profile/management/__init__.py => pytask/profile/management/__init__.py
rename : profile/management/commands/seed_db.py => pytask/profile/management/commands/seed_db.py
rename : profile/models.py => pytask/profile/models.py
rename : profile/templatetags/user_tags.py => pytask/profile/templatetags/user_tags.py
rename : taskapp/tests.py => pytask/profile/tests.py
rename : profile/urls.py => pytask/profile/urls.py
rename : profile/utils.py => pytask/profile/utils.py
rename : profile/views.py => pytask/profile/views.py
rename : static/css/base.css => pytask/static/css/base.css
rename : taskapp/tests.py => pytask/taskapp/tests.py
rename : taskapp/views.py => pytask/taskapp/views.py
rename : templates/base.html => pytask/templates/base.html
rename : templates/profile/browse_notifications.html => pytask/templates/profile/browse_notifications.html
rename : templates/profile/edit.html => pytask/templates/profile/edit.html
rename : templates/profile/view.html => pytask/templates/profile/view.html
rename : templates/profile/view_notification.html => pytask/templates/profile/view_notification.html
rename : templates/registration/activate.html => pytask/templates/registration/activate.html
rename : templates/registration/activation_email.txt => pytask/templates/registration/activation_email.txt
rename : templates/registration/activation_email_subject.txt => pytask/templates/registration/activation_email_subject.txt
rename : templates/registration/logged_out.html => pytask/templates/registration/logged_out.html
rename : templates/registration/login.html => pytask/templates/registration/login.html
rename : templates/registration/logout.html => pytask/templates/registration/logout.html
rename : templates/registration/password_change_done.html => pytask/templates/registration/password_change_done.html
rename : templates/registration/password_change_form.html => pytask/templates/registration/password_change_form.html
rename : templates/registration/password_reset_complete.html => pytask/templates/registration/password_reset_complete.html
rename : templates/registration/password_reset_confirm.html => pytask/templates/registration/password_reset_confirm.html
rename : templates/registration/password_reset_done.html => pytask/templates/registration/password_reset_done.html
rename : templates/registration/password_reset_email.html => pytask/templates/registration/password_reset_email.html
rename : templates/registration/password_reset_form.html => pytask/templates/registration/password_reset_form.html
rename : templates/registration/registration_complete.html => pytask/templates/registration/registration_complete.html
rename : templates/registration/registration_form.html => pytask/templates/registration/registration_form.html
rename : utils.py => pytask/utils.py
Diffstat (limited to 'parts/django/docs/howto')
19 files changed, 4163 insertions, 0 deletions
diff --git a/parts/django/docs/howto/apache-auth.txt b/parts/django/docs/howto/apache-auth.txt new file mode 100644 index 0000000..2ebae0b --- /dev/null +++ b/parts/django/docs/howto/apache-auth.txt @@ -0,0 +1,120 @@ +========================================================= +Authenticating against Django's user database from Apache +========================================================= + +Since keeping multiple authentication databases in sync is a common problem when +dealing with Apache, you can configuring Apache to authenticate against Django's +:doc:`authentication system </topics/auth>` directly. For example, you +could: + + * Serve static/media files directly from Apache only to authenticated users. + + * Authenticate access to a Subversion_ repository against Django users with + a certain permission. + + * Allow certain users to connect to a WebDAV share created with mod_dav_. + +.. _Subversion: http://subversion.tigris.org/ +.. _mod_dav: http://httpd.apache.org/docs/2.0/mod/mod_dav.html + +Configuring Apache +================== + +To check against Django's authorization database from a Apache configuration +file, you'll need to use mod_python's ``PythonAuthenHandler`` directive along +with the standard ``Auth*`` and ``Require`` directives: + +.. code-block:: apache + + <Location /example/> + AuthType Basic + AuthName "example.com" + Require valid-user + + SetEnv DJANGO_SETTINGS_MODULE mysite.settings + PythonAuthenHandler django.contrib.auth.handlers.modpython + </Location> + +.. admonition:: Using the authentication handler with Apache 2.2 + + If you're using Apache 2.2, you'll need to take a couple extra steps. + + You'll need to ensure that ``mod_auth_basic`` and ``mod_authz_user`` + are loaded. These might be compiled statically into Apache, or you might + need to use ``LoadModule`` to load them dynamically (as shown in the + example at the bottom of this note). + + You'll also need to insert configuration directives that prevent Apache + from trying to use other authentication modules, as well as specifying + the ``AuthUserFile`` directive and pointing it to ``/dev/null``. Depending + on which other authentication modules you have loaded, you might need one + or more of the following directives: + + .. code-block:: apache + + AuthBasicAuthoritative Off + AuthDefaultAuthoritative Off + AuthzLDAPAuthoritative Off + AuthzDBMAuthoritative Off + AuthzDefaultAuthoritative Off + AuthzGroupFileAuthoritative Off + AuthzOwnerAuthoritative Off + AuthzUserAuthoritative Off + + A complete configuration, with differences between Apache 2.0 and + Apache 2.2 marked in bold, would look something like: + + .. parsed-literal:: + + **LoadModule auth_basic_module modules/mod_auth_basic.so** + **LoadModule authz_user_module modules/mod_authz_user.so** + + ... + + <Location /example/> + AuthType Basic + AuthName "example.com" + **AuthUserFile /dev/null** + **AuthBasicAuthoritative Off** + Require valid-user + + SetEnv DJANGO_SETTINGS_MODULE mysite.settings + PythonAuthenHandler django.contrib.auth.handlers.modpython + </Location> + +By default, the authentication handler will limit access to the ``/example/`` +location to users marked as staff members. You can use a set of +``PythonOption`` directives to modify this behavior: + + ================================ ========================================= + ``PythonOption`` Explanation + ================================ ========================================= + ``DjangoRequireStaffStatus`` If set to ``on`` only "staff" users (i.e. + those with the ``is_staff`` flag set) + will be allowed. + + Defaults to ``on``. + + ``DjangoRequireSuperuserStatus`` If set to ``on`` only superusers (i.e. + those with the ``is_superuser`` flag set) + will be allowed. + + Defaults to ``off``. + + ``DjangoPermissionName`` The name of a permission to require for + access. See :ref:`custom permissions + <custom-permissions>` for more + information. + + By default no specific permission will be + required. + ================================ ========================================= + +Note that sometimes ``SetEnv`` doesn't play well in this mod_python +configuration, for reasons unknown. If you're having problems getting +mod_python to recognize your ``DJANGO_SETTINGS_MODULE``, you can set it using +``PythonOption`` instead of ``SetEnv``. Therefore, these two Apache directives +are equivalent:: + + SetEnv DJANGO_SETTINGS_MODULE mysite.settings + PythonOption DJANGO_SETTINGS_MODULE mysite.settings diff --git a/parts/django/docs/howto/auth-remote-user.txt b/parts/django/docs/howto/auth-remote-user.txt new file mode 100644 index 0000000..deab794 --- /dev/null +++ b/parts/django/docs/howto/auth-remote-user.txt @@ -0,0 +1,100 @@ +==================================== +Authentication using ``REMOTE_USER`` +==================================== + +.. currentmodule:: django.contrib.auth.backends + +This document describes how to make use of external authentication sources +(where the Web server sets the ``REMOTE_USER`` environment variable) in your +Django applications. This type of authentication solution is typically seen on +intranet sites, with single sign-on solutions such as IIS and Integrated +Windows Authentication or Apache and `mod_authnz_ldap`_, `CAS`_, `Cosign`_, +`WebAuth`_, `mod_auth_sspi`_, etc. + +.. _mod_authnz_ldap: http://httpd.apache.org/docs/2.2/mod/mod_authnz_ldap.html +.. _CAS: http://www.jasig.org/cas +.. _Cosign: http://weblogin.org +.. _WebAuth: http://www.stanford.edu/services/webauth/ +.. _mod_auth_sspi: http://sourceforge.net/projects/mod-auth-sspi + +When the Web server takes care of authentication it typically sets the +``REMOTE_USER`` environment variable for use in the underlying application. In +Django, ``REMOTE_USER`` is made available in the :attr:`request.META +<django.http.HttpRequest.META>` attribute. Django can be configured to make +use of the ``REMOTE_USER`` value using the ``RemoteUserMiddleware`` and +``RemoteUserBackend`` classes found in :mod:`django.contrib.auth`. + +Configuration +============= + +First, you must add the +:class:`django.contrib.auth.middleware.RemoteUserMiddleware` to the +:setting:`MIDDLEWARE_CLASSES` setting **after** the +:class:`django.contrib.auth.middleware.AuthenticationMiddleware`:: + + MIDDLEWARE_CLASSES = ( + ... + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.auth.middleware.RemoteUserMiddleware', + ... + ) + +Next, you must replace the :class:`~django.contrib.auth.backends.ModelBackend` +with ``RemoteUserBackend`` in the :setting:`AUTHENTICATION_BACKENDS` setting:: + + AUTHENTICATION_BACKENDS = ( + 'django.contrib.auth.backends.RemoteUserBackend', + ) + +With this setup, ``RemoteUserMiddleware`` will detect the username in +``request.META['REMOTE_USER']`` and will authenticate and auto-login that user +using the ``RemoteUserBackend``. + +.. note:: + Since the ``RemoteUserBackend`` inherits from ``ModelBackend``, you will + still have all of the same permissions checking that is implemented in + ``ModelBackend``. + +If your authentication mechanism uses a custom HTTP header and not +``REMOTE_USER``, you can subclass ``RemoteUserMiddleware`` and set the +``header`` attribute to the desired ``request.META`` key. For example:: + + from django.contrib.auth.middleware import RemoteUserMiddleware + + class CustomHeaderMiddleware(RemoteUserMiddleware): + header = 'HTTP_AUTHUSER' + + +``RemoteUserBackend`` +===================== + +.. class:: django.contrib.auth.backends.RemoteUserBackend + +If you need more control, you can create your own authentication backend +that inherits from ``RemoteUserBackend`` and overrides certain parts: + +Attributes +~~~~~~~~~~ + +.. attribute:: RemoteUserBackend.create_unknown_user + + ``True`` or ``False``. Determines whether or not a + :class:`~django.contrib.auth.models.User` object is created if not already + in the database. Defaults to ``True``. + +Methods +~~~~~~~ + +.. method:: RemoteUserBackend.clean_username(username) + + Performs any cleaning on the ``username`` (e.g. stripping LDAP DN + information) prior to using it to get or create a + :class:`~django.contrib.auth.models.User` object. Returns the cleaned + username. + +.. method:: RemoteUserBackend.configure_user(user) + + Configures a newly created user. This method is called immediately after a + new user is created, and can be used to perform custom setup actions, such + as setting the user's groups based on attributes in an LDAP directory. + Returns the user object. diff --git a/parts/django/docs/howto/custom-file-storage.txt b/parts/django/docs/howto/custom-file-storage.txt new file mode 100644 index 0000000..1b0f32f --- /dev/null +++ b/parts/django/docs/howto/custom-file-storage.txt @@ -0,0 +1,90 @@ +Writing a custom storage system +=============================== + +.. currentmodule:: django.core.files.storage + +If you need to provide custom file storage -- a common example is storing files +on some remote system -- you can do so by defining a custom storage class. +You'll need to follow these steps: + +#. Your custom storage system must be a subclass of + ``django.core.files.storage.Storage``:: + + from django.core.files.storage import Storage + + class MyStorage(Storage): + ... + +#. Django must be able to instantiate your storage system without any arguments. + This means that any settings should be taken from ``django.conf.settings``:: + + from django.conf import settings + from django.core.files.storage import Storage + + class MyStorage(Storage): + def __init__(self, option=None): + if not option: + option = settings.CUSTOM_STORAGE_OPTIONS + ... + +#. Your storage class must implement the ``_open()`` and ``_save()`` methods, + along with any other methods appropriate to your storage class. See below for + more on these methods. + + In addition, if your class provides local file storage, it must override + the ``path()`` method. + +Your custom storage system may override any of the storage methods explained in +:doc:`/ref/files/storage`, but you **must** implement the following methods: + + * :meth:`Storage.delete` + * :meth:`Storage.exists` + * :meth:`Storage.listdir` + * :meth:`Storage.size` + * :meth:`Storage.url` + +You'll also usually want to use hooks specifically designed for custom storage +objects. These are: + +``_open(name, mode='rb')`` +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Required**. + +Called by ``Storage.open()``, this is the actual mechanism the storage class +uses to open the file. This must return a ``File`` object, though in most cases, +you'll want to return some subclass here that implements logic specific to the +backend storage system. + +``_save(name, content)`` +~~~~~~~~~~~~~~~~~~~~~~~~ + +Called by ``Storage.save()``. The ``name`` will already have gone through +``get_valid_name()`` and ``get_available_name()``, and the ``content`` will be a +``File`` object itself. + +Should return the actual name of name of the file saved (usually the ``name`` +passed in, but if the storage needs to change the file name return the new name +instead). + +``get_valid_name(name)`` +------------------------ + +Returns a filename suitable for use with the underlying storage system. The +``name`` argument passed to this method is the original filename sent to the +server, after having any path information removed. Override this to customize +how non-standard characters are converted to safe filenames. + +The code provided on ``Storage`` retains only alpha-numeric characters, periods +and underscores from the original filename, removing everything else. + +``get_available_name(name)`` +---------------------------- + +Returns a filename that is available in the storage mechanism, possibly taking +the provided filename into account. The ``name`` argument passed to this method +will have already cleaned to a filename valid for the storage system, according +to the ``get_valid_name()`` method described above. + +The code provided on ``Storage`` simply appends ``"_1"``, ``"_2"``, etc. to the +filename until it finds one that's available in the destination directory. diff --git a/parts/django/docs/howto/custom-management-commands.txt b/parts/django/docs/howto/custom-management-commands.txt new file mode 100644 index 0000000..4a1747f --- /dev/null +++ b/parts/django/docs/howto/custom-management-commands.txt @@ -0,0 +1,253 @@ +==================================== +Writing custom django-admin commands +==================================== + +.. versionadded:: 1.0 + +Applications can register their own actions with ``manage.py``. For example, +you might want to add a ``manage.py`` action for a Django app that you're +distributing. In this document, we will be building a custom ``closepoll`` +command for the ``polls`` application from the +:doc:`tutorial</intro/tutorial01>`. + +To do this, just add a ``management/commands`` directory to the application. +Each Python module in that directory will be auto-discovered and registered as +a command that can be executed as an action when you run ``manage.py``:: + + polls/ + __init__.py + models.py + management/ + __init__.py + commands/ + __init__.py + closepoll.py + tests.py + views.py + +In this example, the ``closepoll`` command will be made available to any project +that includes the ``polls`` application in :setting:`INSTALLED_APPS`. + +The ``closepoll.py`` module has only one requirement -- it must define a class +``Command`` that extends :class:`BaseCommand` or one of its +:ref:`subclasses<ref-basecommand-subclasses>`. + +.. admonition:: Standalone scripts + + Custom management commands are especially useful for running standalone + scripts or for scripts that are periodically executed from the UNIX crontab + or from Windows scheduled tasks control panel. + +To implement the command, edit ``polls/management/commands/closepoll.py`` to +look like this: + +.. code-block:: python + + from django.core.management.base import BaseCommand, CommandError + from example.polls.models import Poll + + class Command(BaseCommand): + args = '<poll_id poll_id ...>' + help = 'Closes the specified poll for voting' + + def handle(self, *args, **options): + for poll_id in args: + try: + poll = Poll.objects.get(pk=int(poll_id)) + except Poll.DoesNotExist: + raise CommandError('Poll "%s" does not exist' % poll_id) + + poll.opened = False + poll.save() + + print 'Successfully closed poll "%s"' % poll_id + +The new custom command can be called using ``python manage.py closepoll +<poll_id>``. + +The ``handle()`` method takes zero or more ``poll_ids`` and sets ``poll.opened`` +to ``False`` for each one. If the user referenced any nonexistant polls, a +:class:`CommandError` is raised. The ``poll.opened`` attribute does not exist +in the :doc:`tutorial</intro/tutorial01>` and was added to +``polls.models.Poll`` for this example. + +The same ``closepoll`` could be easily modified to delete a given poll instead +of closing it by accepting additional command line options. These custom options +must be added to :attr:`~BaseCommand.option_list` like this: + +.. code-block:: python + + from optparse import make_option + + class Command(BaseCommand): + option_list = BaseCommand.option_list + ( + make_option('--delete', + action='store_true', + dest='delete', + default=False, + help='Delete poll instead of closing it'), + ) + # ... + +In addition to being able to add custom command line options, all +:doc:`management commands</ref/django-admin>` can accept some +default options such as :djadminopt:`--verbosity` and :djadminopt:`--traceback`. + +Command objects +=============== + +.. class:: BaseCommand + +The base class from which all management commands ultimately derive. + +Use this class if you want access to all of the mechanisms which +parse the command-line arguments and work out what code to call in +response; if you don't need to change any of that behavior, +consider using one of its :ref:`subclasses<ref-basecommand-subclasses>`. + +Subclassing the :class:`BaseCommand` class requires that you implement the +:meth:`~BaseCommand.handle` method. + +Attributes +---------- + +All attributes can be set in your derived class and can be used in +:class:`BaseCommand`'s :ref:`subclasses<ref-basecommand-subclasses>`. + +.. attribute:: BaseCommand.args + + A string listing the arguments accepted by the command, + suitable for use in help messages; e.g., a command which takes + a list of application names might set this to '<appname + appname ...>'. + +.. attribute:: BaseCommand.can_import_settings + + A boolean indicating whether the command needs to be able to + import Django settings; if ``True``, ``execute()`` will verify + that this is possible before proceeding. Default value is + ``True``. + +.. attribute:: BaseCommand.help + + A short description of the command, which will be printed in the + help message when the user runs the command + ``python manage.py help <command>``. + +.. attribute:: BaseCommand.option_list + + This is the list of ``optparse`` options which will be fed + into the command's ``OptionParser`` for parsing arguments. + +.. attribute:: BaseCommand.output_transaction + + A boolean indicating whether the command outputs SQL + statements; if ``True``, the output will automatically be + wrapped with ``BEGIN;`` and ``COMMIT;``. Default value is + ``False``. + +.. attribute:: BaseCommand.requires_model_validation + + A boolean; if ``True``, validation of installed models will be + performed prior to executing the command. Default value is + ``True``. To validate an individual application's models + rather than all applications' models, call + :meth:`~BaseCommand.validate` from :meth:`~BaseCommand.handle`. + +Methods +------- + +:class:`BaseCommand` has a few methods that can be overridden but only +the :meth:`~BaseCommand.handle` method must be implemented. + +.. admonition:: Implementing a constructor in a subclass + + If you implement ``__init__`` in your subclass of :class:`BaseCommand`, + you must call :class:`BaseCommand`'s ``__init__``. + + .. code-block:: python + + class Command(BaseCommand): + def __init__(self, *args, **kwargs): + super(Command, self).__init__(*args, **kwargs) + # ... + +.. method:: BaseCommand.get_version() + + Return the Django version, which should be correct for all + built-in Django commands. User-supplied commands can + override this method to return their own version. + +.. method:: BaseCommand.execute(*args, **options) + + Try to execute this command, performing model validation if + needed (as controlled by the attribute + :attr:`requires_model_validation`). If the command raises a + :class:`CommandError`, intercept it and print it sensibly to + stderr. + +.. method:: BaseCommand.handle(*args, **options) + + The actual logic of the command. Subclasses must implement this method. + +.. _ref-basecommand-subclasses: + +BaseCommand subclasses +---------------------- + +.. class:: AppCommand + +A management command which takes one or more installed application +names as arguments, and does something with each of them. + +Rather than implementing :meth:`~BaseCommand.handle`, subclasses must implement +:meth:`~AppCommand.handle_app`, which will be called once for each application. + +.. method:: AppCommand.handle_app(app, **options) + + Perform the command's actions for ``app``, which will be the + Python module corresponding to an application name given on + the command line. + +.. class:: LabelCommand + +A management command which takes one or more arbitrary arguments +(labels) on the command line, and does something with each of +them. + +Rather than implementing :meth:`~BaseCommand.handle`, subclasses must implement +:meth:`~LabelCommand.handle_label`, which will be called once for each label. + +.. method:: LabelCommand.handle_label(label, **options) + + Perform the command's actions for ``label``, which will be the + string as given on the command line. + +.. class:: NoArgsCommand + +A command which takes no arguments on the command line. + +Rather than implementing :meth:`~BaseCommand.handle`, subclasses must implement +:meth:`~NoArgsCommand.handle_noargs`; :meth:`~BaseCommand.handle` itself is +overridden to ensure no arguments are passed to the command. + +.. method:: NoArgsCommand.handle_noargs(**options) + + Perform this command's actions + +.. _ref-command-exceptions: + +Command exceptions +------------------ + +.. class:: CommandError + +Exception class indicating a problem while executing a management +command. + +If this exception is raised during the execution of a management +command, it will be caught and turned into a nicely-printed error +message to the appropriate output stream (i.e., stderr); as a +result, raising this exception (with a sensible description of the +error) is the preferred way to indicate that something has gone +wrong in the execution of a command. diff --git a/parts/django/docs/howto/custom-model-fields.txt b/parts/django/docs/howto/custom-model-fields.txt new file mode 100644 index 0000000..1840c5b --- /dev/null +++ b/parts/django/docs/howto/custom-model-fields.txt @@ -0,0 +1,745 @@ +=========================== +Writing custom model fields +=========================== + +.. versionadded:: 1.0 +.. currentmodule:: django.db.models + +Introduction +============ + +The :doc:`model reference </topics/db/models>` documentation explains how to use +Django's standard field classes -- :class:`~django.db.models.CharField`, +:class:`~django.db.models.DateField`, etc. For many purposes, those classes are +all you'll need. Sometimes, though, the Django version won't meet your precise +requirements, or you'll want to use a field that is entirely different from +those shipped with Django. + +Django's built-in field types don't cover every possible database column type -- +only the common types, such as ``VARCHAR`` and ``INTEGER``. For more obscure +column types, such as geographic polygons or even user-created types such as +`PostgreSQL custom types`_, you can define your own Django ``Field`` subclasses. + +.. _PostgreSQL custom types: http://www.postgresql.org/docs/8.2/interactive/sql-createtype.html + +Alternatively, you may have a complex Python object that can somehow be +serialized to fit into a standard database column type. This is another case +where a ``Field`` subclass will help you use your object with your models. + +Our example object +------------------ + +Creating custom fields requires a bit of attention to detail. To make things +easier to follow, we'll use a consistent example throughout this document: +wrapping a Python object representing the deal of cards in a hand of Bridge_. +Don't worry, you don't have know how to play Bridge to follow this example. +You only need to know that 52 cards are dealt out equally to four players, who +are traditionally called *north*, *east*, *south* and *west*. Our class looks +something like this:: + + class Hand(object): + """A hand of cards (bridge style)""" + + def __init__(self, north, east, south, west): + # Input parameters are lists of cards ('Ah', '9s', etc) + self.north = north + self.east = east + self.south = south + self.west = west + + # ... (other possibly useful methods omitted) ... + +.. _Bridge: http://en.wikipedia.org/wiki/Contract_bridge + +This is just an ordinary Python class, with nothing Django-specific about it. +We'd like to be able to do things like this in our models (we assume the +``hand`` attribute on the model is an instance of ``Hand``):: + + example = MyModel.objects.get(pk=1) + print example.hand.north + + new_hand = Hand(north, east, south, west) + example.hand = new_hand + example.save() + +We assign to and retrieve from the ``hand`` attribute in our model just like +any other Python class. The trick is to tell Django how to handle saving and +loading such an object. + +In order to use the ``Hand`` class in our models, we **do not** have to change +this class at all. This is ideal, because it means you can easily write +model support for existing classes where you cannot change the source code. + +.. note:: + You might only be wanting to take advantage of custom database column + types and deal with the data as standard Python types in your models; + strings, or floats, for example. This case is similar to our ``Hand`` + example and we'll note any differences as we go along. + +Background theory +================= + +Database storage +---------------- + +The simplest way to think of a model field is that it provides a way to take a +normal Python object -- string, boolean, ``datetime``, or something more +complex like ``Hand`` -- and convert it to and from a format that is useful +when dealing with the database (and serialization, but, as we'll see later, +that falls out fairly naturally once you have the database side under control). + +Fields in a model must somehow be converted to fit into an existing database +column type. Different databases provide different sets of valid column types, +but the rule is still the same: those are the only types you have to work +with. Anything you want to store in the database must fit into one of +those types. + +Normally, you're either writing a Django field to match a particular database +column type, or there's a fairly straightforward way to convert your data to, +say, a string. + +For our ``Hand`` example, we could convert the card data to a string of 104 +characters by concatenating all the cards together in a pre-determined order -- +say, all the *north* cards first, then the *east*, *south* and *west* cards. So +``Hand`` objects can be saved to text or character columns in the database. + +What does a field class do? +--------------------------- + +All of Django's fields (and when we say *fields* in this document, we always +mean model fields and not :doc:`form fields </ref/forms/fields>`) are subclasses +of :class:`django.db.models.Field`. Most of the information that Django records +about a field is common to all fields -- name, help text, uniqueness and so +forth. Storing all that information is handled by ``Field``. We'll get into the +precise details of what ``Field`` can do later on; for now, suffice it to say +that everything descends from ``Field`` and then customizes key pieces of the +class behavior. + +It's important to realize that a Django field class is not what is stored in +your model attributes. The model attributes contain normal Python objects. The +field classes you define in a model are actually stored in the ``Meta`` class +when the model class is created (the precise details of how this is done are +unimportant here). This is because the field classes aren't necessary when +you're just creating and modifying attributes. Instead, they provide the +machinery for converting between the attribute value and what is stored in the +database or sent to the :doc:`serializer </topics/serialization>`. + +Keep this in mind when creating your own custom fields. The Django ``Field`` +subclass you write provides the machinery for converting between your Python +instances and the database/serializer values in various ways (there are +differences between storing a value and using a value for lookups, for +example). If this sounds a bit tricky, don't worry -- it will become clearer in +the examples below. Just remember that you will often end up creating two +classes when you want a custom field: + + * The first class is the Python object that your users will manipulate. + They will assign it to the model attribute, they will read from it for + displaying purposes, things like that. This is the ``Hand`` class in our + example. + + * The second class is the ``Field`` subclass. This is the class that knows + how to convert your first class back and forth between its permanent + storage form and the Python form. + +Writing a field subclass +======================== + +When planning your :class:`~django.db.models.Field` subclass, first give some +thought to which existing :class:`~django.db.models.Field` class your new field +is most similar to. Can you subclass an existing Django field and save yourself +some work? If not, you should subclass the :class:`~django.db.models.Field` +class, from which everything is descended. + +Initializing your new field is a matter of separating out any arguments that are +specific to your case from the common arguments and passing the latter to the +:meth:`~django.db.models.Field.__init__` method of +:class:`~django.db.models.Field` (or your parent class). + +In our example, we'll call our field ``HandField``. (It's a good idea to call +your :class:`~django.db.models.Field` subclass ``<Something>Field``, so it's +easily identifiable as a :class:`~django.db.models.Field` subclass.) It doesn't +behave like any existing field, so we'll subclass directly from +:class:`~django.db.models.Field`:: + + from django.db import models + + class HandField(models.Field): + + description = "A hand of cards (bridge style)" + + def __init__(self, *args, **kwargs): + kwargs['max_length'] = 104 + super(HandField, self).__init__(*args, **kwargs) + +Our ``HandField`` accepts most of the standard field options (see the list +below), but we ensure it has a fixed length, since it only needs to hold 52 +card values plus their suits; 104 characters in total. + +.. note:: + Many of Django's model fields accept options that they don't do anything + with. For example, you can pass both + :attr:`~django.db.models.Field.editable` and + :attr:`~django.db.models.Field.auto_now` to a + :class:`django.db.models.DateField` and it will simply ignore the + :attr:`~django.db.models.Field.editable` parameter + (:attr:`~django.db.models.Field.auto_now` being set implies + ``editable=False``). No error is raised in this case. + + This behavior simplifies the field classes, because they don't need to + check for options that aren't necessary. They just pass all the options to + the parent class and then don't use them later on. It's up to you whether + you want your fields to be more strict about the options they select, or + to use the simpler, more permissive behavior of the current fields. + +The :meth:`~django.db.models.Field.__init__` method takes the following +parameters: + + * :attr:`~django.db.models.Field.verbose_name` + * :attr:`~django.db.models.Field.name` + * :attr:`~django.db.models.Field.primary_key` + * :attr:`~django.db.models.Field.max_length` + * :attr:`~django.db.models.Field.unique` + * :attr:`~django.db.models.Field.blank` + * :attr:`~django.db.models.Field.null` + * :attr:`~django.db.models.Field.db_index` + * :attr:`~django.db.models.Field.rel`: Used for related fields (like + :class:`ForeignKey`). For advanced use only. + * :attr:`~django.db.models.Field.default` + * :attr:`~django.db.models.Field.editable` + * :attr:`~django.db.models.Field.serialize`: If ``False``, the field will + not be serialized when the model is passed to Django's :doc:`serializers + </topics/serialization>`. Defaults to ``True``. + * :attr:`~django.db.models.Field.unique_for_date` + * :attr:`~django.db.models.Field.unique_for_month` + * :attr:`~django.db.models.Field.unique_for_year` + * :attr:`~django.db.models.Field.choices` + * :attr:`~django.db.models.Field.help_text` + * :attr:`~django.db.models.Field.db_column` + * :attr:`~django.db.models.Field.db_tablespace`: Currently only used with + the Oracle backend and only for index creation. You can usually ignore + this option. + * :attr:`~django.db.models.Field.auto_created`: True if the field was + automatically created, as for the `OneToOneField` used by model + inheritance. For advanced use only. + +All of the options without an explanation in the above list have the same +meaning they do for normal Django fields. See the :doc:`field documentation +</ref/models/fields>` for examples and details. + +The ``SubfieldBase`` metaclass +------------------------------ + +As we indicated in the introduction_, field subclasses are often needed for +two reasons: either to take advantage of a custom database column type, or to +handle complex Python types. Obviously, a combination of the two is also +possible. If you're only working with custom database column types and your +model fields appear in Python as standard Python types direct from the +database backend, you don't need to worry about this section. + +If you're handling custom Python types, such as our ``Hand`` class, we need to +make sure that when Django initializes an instance of our model and assigns a +database value to our custom field attribute, we convert that value into the +appropriate Python object. The details of how this happens internally are a +little complex, but the code you need to write in your ``Field`` class is +simple: make sure your field subclass uses a special metaclass: + +.. class:: django.db.models.SubfieldBase + +For example:: + + class HandField(models.Field): + + description = "A hand of cards (bridge style)" + + __metaclass__ = models.SubfieldBase + + def __init__(self, *args, **kwargs): + # ... + +This ensures that the :meth:`to_python` method, documented below, will always be +called when the attribute is initialized. + +ModelForms and custom fields +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you use :class:`~django.db.models.SubfieldBase`, :meth:`to_python` +will be called every time an instance of the field is assigned a +value. This means that whenever a value may be assigned to the field, +you need to ensure that it will be of the correct datatype, or that +you handle any exceptions. + +This is especially important if you use :doc:`ModelForms +</topics/forms/modelforms>`. When saving a ModelForm, Django will use +form values to instantiate model instances. However, if the cleaned +form data can't be used as valid input to the field, the normal form +validation process will break. + +Therefore, you must ensure that the form field used to represent your +custom field performs whatever input validation and data cleaning is +necessary to convert user-provided form input into a +`to_python()`-compatible model field value. This may require writing a +custom form field, and/or implementing the :meth:`formfield` method on +your field to return a form field class whose `to_python()` returns the +correct datatype. + +Documenting your Custom Field +----------------------------- + +.. class:: django.db.models.Field + +.. attribute:: description + +As always, you should document your field type, so users will know what it is. +In addition to providing a docstring for it, which is useful for developers, +you can also allow users of the admin app to see a short description of the +field type via the :doc:`django.contrib.admindocs +</ref/contrib/admin/admindocs>` application. To do this simply provide +descriptive text in a ``description`` class attribute of your custom field. In +the above example, the type description displayed by the ``admindocs`` +application for a ``HandField`` will be 'A hand of cards (bridge style)'. + +Useful methods +-------------- + +Once you've created your :class:`~django.db.models.Field` subclass and set up +the ``__metaclass__``, you might consider overriding a few standard methods, +depending on your field's behavior. The list of methods below is in +approximately decreasing order of importance, so start from the top. + +Custom database types +~~~~~~~~~~~~~~~~~~~~~ + +.. method:: db_type(self, connection) + +.. versionadded:: 1.2 + The ``connection`` argument was added to support multiple databases. + +Returns the database column data type for the :class:`~django.db.models.Field`, +taking into account the connection object, and the settings associated with it. + +Say you've created a PostgreSQL custom type called ``mytype``. You can use this +field with Django by subclassing ``Field`` and implementing the :meth:`db_type` +method, like so:: + + from django.db import models + + class MytypeField(models.Field): + def db_type(self, connection): + return 'mytype' + +Once you have ``MytypeField``, you can use it in any model, just like any other +``Field`` type:: + + class Person(models.Model): + name = models.CharField(max_length=80) + gender = models.CharField(max_length=1) + something_else = MytypeField() + +If you aim to build a database-agnostic application, you should account for +differences in database column types. For example, the date/time column type +in PostgreSQL is called ``timestamp``, while the same column in MySQL is called +``datetime``. The simplest way to handle this in a ``db_type()`` method is to +check the ``connection.settings_dict['ENGINE']`` attribute. + +For example:: + + class MyDateField(models.Field): + def db_type(self, connection): + if connection.settings_dict['ENGINE'] == 'django.db.backends.mysql': + return 'datetime' + else: + return 'timestamp' + +The :meth:`db_type` method is only called by Django when the framework +constructs the ``CREATE TABLE`` statements for your application -- that is, when +you first create your tables. It's not called at any other time, so it can +afford to execute slightly complex code, such as the ``connection.settings_dict`` +check in the above example. + +Some database column types accept parameters, such as ``CHAR(25)``, where the +parameter ``25`` represents the maximum column length. In cases like these, +it's more flexible if the parameter is specified in the model rather than being +hard-coded in the ``db_type()`` method. For example, it wouldn't make much +sense to have a ``CharMaxlength25Field``, shown here:: + + # This is a silly example of hard-coded parameters. + class CharMaxlength25Field(models.Field): + def db_type(self, connection): + return 'char(25)' + + # In the model: + class MyModel(models.Model): + # ... + my_field = CharMaxlength25Field() + +The better way of doing this would be to make the parameter specifiable at run +time -- i.e., when the class is instantiated. To do that, just implement +:meth:`django.db.models.Field.__init__`, like so:: + + # This is a much more flexible example. + class BetterCharField(models.Field): + def __init__(self, max_length, *args, **kwargs): + self.max_length = max_length + super(BetterCharField, self).__init__(*args, **kwargs) + + def db_type(self, connection): + return 'char(%s)' % self.max_length + + # In the model: + class MyModel(models.Model): + # ... + my_field = BetterCharField(25) + +Finally, if your column requires truly complex SQL setup, return ``None`` from +:meth:`db_type`. This will cause Django's SQL creation code to skip over this +field. You are then responsible for creating the column in the right table in +some other way, of course, but this gives you a way to tell Django to get out of +the way. + +Converting database values to Python objects +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. method:: to_python(self, value) + +Converts a value as returned by your database (or a serializer) to a Python +object. + +The default implementation simply returns ``value``, for the common case in +which the database backend already returns data in the correct format (as a +Python string, for example). + +If your custom :class:`~django.db.models.Field` class deals with data structures +that are more complex than strings, dates, integers or floats, then you'll need +to override this method. As a general rule, the method should deal gracefully +with any of the following arguments: + + * An instance of the correct type (e.g., ``Hand`` in our ongoing example). + + * A string (e.g., from a deserializer). + + * Whatever the database returns for the column type you're using. + +In our ``HandField`` class, we're storing the data as a VARCHAR field in the +database, so we need to be able to process strings and ``Hand`` instances in +:meth:`to_python`:: + + import re + + class HandField(models.Field): + # ... + + def to_python(self, value): + if isinstance(value, Hand): + return value + + # The string case. + p1 = re.compile('.{26}') + p2 = re.compile('..') + args = [p2.findall(x) for x in p1.findall(value)] + return Hand(*args) + +Notice that we always return a ``Hand`` instance from this method. That's the +Python object type we want to store in the model's attribute. + +**Remember:** If your custom field needs the :meth:`to_python` method to be +called when it is created, you should be using `The SubfieldBase metaclass`_ +mentioned earlier. Otherwise :meth:`to_python` won't be called automatically. + +Converting Python objects to query values +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. method:: get_prep_value(self, value) + +.. versionadded:: 1.2 + This method was factored out of ``get_db_prep_value()`` + +This is the reverse of :meth:`to_python` when working with the +database backends (as opposed to serialization). The ``value`` +parameter is the current value of the model's attribute (a field has +no reference to its containing model, so it cannot retrieve the value +itself), and the method should return data in a format that has been +prepared for use as a parameter in a query. + +This conversion should *not* include any database-specific +conversions. If database-specific conversions are required, they +should be made in the call to :meth:`get_db_prep_value`. + +For example:: + + class HandField(models.Field): + # ... + + def get_prep_value(self, value): + return ''.join([''.join(l) for l in (value.north, + value.east, value.south, value.west)]) + +Converting query values to database values +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. method:: get_db_prep_value(self, value, connection, prepared=False) + +.. versionadded:: 1.2 + The ``connection`` and ``prepared`` arguments were added to support multiple databases. + +Some data types (for example, dates) need to be in a specific format +before they can be used by a database backend. +:meth:`get_db_prep_value` is the method where those conversions should +be made. The specific connection that will be used for the query is +passed as the ``connection`` parameter. This allows you to use +backend-specific conversion logic if it is required. + +The ``prepared`` argument describes whether or not the value has +already been passed through :meth:`get_prep_value` conversions. When +``prepared`` is False, the default implementation of +:meth:`get_db_prep_value` will call :meth:`get_prep_value` to do +initial data conversions before performing any database-specific +processing. + +.. method:: get_db_prep_save(self, value, connection) + +.. versionadded:: 1.2 + The ``connection`` argument was added to support multiple databases. + +Same as the above, but called when the Field value must be *saved* to +the database. As the default implementation just calls +``get_db_prep_value``, you shouldn't need to implement this method +unless your custom field needs a special conversion when being saved +that is not the same as the conversion used for normal query +parameters (which is implemented by ``get_db_prep_value``). + +Preprocessing values before saving +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. method:: pre_save(self, model_instance, add) + +This method is called just prior to :meth:`get_db_prep_save` and should return +the value of the appropriate attribute from ``model_instance`` for this field. +The attribute name is in ``self.attname`` (this is set up by +:class:`~django.db.models.Field`). If the model is being saved to the database +for the first time, the ``add`` parameter will be ``True``, otherwise it will be +``False``. + +You only need to override this method if you want to preprocess the value +somehow, just before saving. For example, Django's +:class:`~django.db.models.DateTimeField` uses this method to set the attribute +correctly in the case of :attr:`~django.db.models.Field.auto_now` or +:attr:`~django.db.models.Field.auto_now_add`. + +If you do override this method, you must return the value of the attribute at +the end. You should also update the model's attribute if you make any changes +to the value so that code holding references to the model will always see the +correct value. + +Preparing values for use in database lookups +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +As with value conversions, preparing a value for database lookups is a +two phase process. + +.. method:: get_prep_lookup(self, lookup_type, value) + +.. versionadded:: 1.2 + This method was factored out of ``get_db_prep_lookup()`` + +:meth:`get_prep_lookup` performs the first phase of lookup preparation, +performing generic data validity checks + +Prepares the ``value`` for passing to the database when used in a lookup (a +``WHERE`` constraint in SQL). The ``lookup_type`` will be one of the valid +Django filter lookups: ``exact``, ``iexact``, ``contains``, ``icontains``, +``gt``, ``gte``, ``lt``, ``lte``, ``in``, ``startswith``, ``istartswith``, +``endswith``, ``iendswith``, ``range``, ``year``, ``month``, ``day``, +``isnull``, ``search``, ``regex``, and ``iregex``. + +Your method must be prepared to handle all of these ``lookup_type`` values and +should raise either a ``ValueError`` if the ``value`` is of the wrong sort (a +list when you were expecting an object, for example) or a ``TypeError`` if +your field does not support that type of lookup. For many fields, you can get +by with handling the lookup types that need special handling for your field +and pass the rest to the :meth:`get_db_prep_lookup` method of the parent class. + +If you needed to implement ``get_db_prep_save()``, you will usually need to +implement ``get_prep_lookup()``. If you don't, ``get_prep_value`` will be +called by the default implementation, to manage ``exact``, ``gt``, ``gte``, +``lt``, ``lte``, ``in`` and ``range`` lookups. + +You may also want to implement this method to limit the lookup types that could +be used with your custom field type. + +Note that, for ``range`` and ``in`` lookups, ``get_prep_lookup`` will receive +a list of objects (presumably of the right type) and will need to convert them +to a list of things of the right type for passing to the database. Most of the +time, you can reuse ``get_prep_value()``, or at least factor out some common +pieces. + +For example, the following code implements ``get_prep_lookup`` to limit the +accepted lookup types to ``exact`` and ``in``:: + + class HandField(models.Field): + # ... + + def get_prep_lookup(self, lookup_type, value): + # We only handle 'exact' and 'in'. All others are errors. + if lookup_type == 'exact': + return self.get_prep_value(value) + elif lookup_type == 'in': + return [self.get_prep_value(v) for v in value] + else: + raise TypeError('Lookup type %r not supported.' % lookup_type) + +.. method:: get_db_prep_lookup(self, lookup_type, value, connection, prepared=False) + +.. versionadded:: 1.2 + The ``connection`` and ``prepared`` arguments were added to support multiple databases. + +Performs any database-specific data conversions required by a lookup. +As with :meth:`get_db_prep_value`, the specific connection that will +be used for the query is passed as the ``connection`` parameter. +The ``prepared`` argument describes whether the value has already been +prepared with :meth:`get_prep_lookup`. + +Specifying the form field for a model field +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. method:: formfield(self, form_class=forms.CharField, **kwargs) + +Returns the default form field to use when this field is displayed in a model. +This method is called by the :class:`~django.forms.ModelForm` helper. + +All of the ``kwargs`` dictionary is passed directly to the form field's +:meth:`~django.forms.Field__init__` method. Normally, all you need to do is +set up a good default for the ``form_class`` argument and then delegate further +handling to the parent class. This might require you to write a custom form +field (and even a form widget). See the :doc:`forms documentation +</topics/forms/index>` for information about this, and take a look at the code in +:mod:`django.contrib.localflavor` for some examples of custom widgets. + +Continuing our ongoing example, we can write the :meth:`formfield` method as:: + + class HandField(models.Field): + # ... + + def formfield(self, **kwargs): + # This is a fairly standard way to set up some defaults + # while letting the caller override them. + defaults = {'form_class': MyFormField} + defaults.update(kwargs) + return super(HandField, self).formfield(**defaults) + +This assumes we've imported a ``MyFormField`` field class (which has its own +default widget). This document doesn't cover the details of writing custom form +fields. + +.. _helper functions: ../forms/#generating-forms-for-models +.. _forms documentation: ../forms/ + +Emulating built-in field types +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. method:: get_internal_type(self) + +Returns a string giving the name of the :class:`~django.db.models.Field` +subclass we are emulating at the database level. This is used to determine the +type of database column for simple cases. + +If you have created a :meth:`db_type` method, you don't need to worry about +:meth:`get_internal_type` -- it won't be used much. Sometimes, though, your +database storage is similar in type to some other field, so you can use that +other field's logic to create the right column. + +For example:: + + class HandField(models.Field): + # ... + + def get_internal_type(self): + return 'CharField' + +No matter which database backend we are using, this will mean that ``syncdb`` +and other SQL commands create the right column type for storing a string. + +If :meth:`get_internal_type` returns a string that is not known to Django for +the database backend you are using -- that is, it doesn't appear in +``django.db.backends.<db_name>.creation.DATA_TYPES`` -- the string will still be +used by the serializer, but the default :meth:`db_type` method will return +``None``. See the documentation of :meth:`db_type` for reasons why this might be +useful. Putting a descriptive string in as the type of the field for the +serializer is a useful idea if you're ever going to be using the serializer +output in some other place, outside of Django. + +Converting field data for serialization +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. method:: value_to_string(self, obj) + +This method is used by the serializers to convert the field into a string for +output. Calling :meth:`Field._get_val_from_obj(obj)` is the best way to get the +value to serialize. For example, since our ``HandField`` uses strings for its +data storage anyway, we can reuse some existing conversion code:: + + class HandField(models.Field): + # ... + + def value_to_string(self, obj): + value = self._get_val_from_obj(obj) + return self.get_db_prep_value(value) + +Some general advice +-------------------- + +Writing a custom field can be a tricky process, particularly if you're doing +complex conversions between your Python types and your database and +serialization formats. Here are a couple of tips to make things go more +smoothly: + + 1. Look at the existing Django fields (in + :file:`django/db/models/fields/__init__.py`) for inspiration. Try to find + a field that's similar to what you want and extend it a little bit, + instead of creating an entirely new field from scratch. + + 2. Put a :meth:`__str__` or :meth:`__unicode__` method on the class you're + wrapping up as a field. There are a lot of places where the default + behavior of the field code is to call + :func:`~django.utils.encoding.force_unicode` on the value. (In our + examples in this document, ``value`` would be a ``Hand`` instance, not a + ``HandField``). So if your :meth:`__unicode__` method automatically + converts to the string form of your Python object, you can save yourself + a lot of work. + + +Writing a ``FileField`` subclass +================================= + +In addition to the above methods, fields that deal with files have a few other +special requirements which must be taken into account. The majority of the +mechanics provided by ``FileField``, such as controlling database storage and +retrieval, can remain unchanged, leaving subclasses to deal with the challenge +of supporting a particular type of file. + +Django provides a ``File`` class, which is used as a proxy to the file's +contents and operations. This can be subclassed to customize how the file is +accessed, and what methods are available. It lives at +``django.db.models.fields.files``, and its default behavior is explained in the +:doc:`file documentation </ref/files/file>`. + +Once a subclass of ``File`` is created, the new ``FileField`` subclass must be +told to use it. To do so, simply assign the new ``File`` subclass to the special +``attr_class`` attribute of the ``FileField`` subclass. + +A few suggestions +------------------ + +In addition to the above details, there are a few guidelines which can greatly +improve the efficiency and readability of the field's code. + + 1. The source for Django's own ``ImageField`` (in + ``django/db/models/fields/files.py``) is a great example of how to + subclass ``FileField`` to support a particular type of file, as it + incorporates all of the techniques described above. + + 2. Cache file attributes wherever possible. Since files may be stored in + remote storage systems, retrieving them may cost extra time, or even + money, that isn't always necessary. Once a file is retrieved to obtain + some data about its content, cache as much of that data as possible to + reduce the number of times the file must be retrieved on subsequent + calls for that information. diff --git a/parts/django/docs/howto/custom-template-tags.txt b/parts/django/docs/howto/custom-template-tags.txt new file mode 100644 index 0000000..95ce274 --- /dev/null +++ b/parts/django/docs/howto/custom-template-tags.txt @@ -0,0 +1,939 @@ +================================ +Custom template tags and filters +================================ + +Introduction +============ + +Django's template system comes with a wide variety of :doc:`built-in +tags and filters </ref/templates/builtins>` designed to address the +presentation logic needs of your application. Nevertheless, you may +find yourself needing functionality that is not covered by the core +set of template primitives. You can extend the template engine by +defining custom tags and filters using Python, and then make them +available to your templates using the ``{% load %}`` tag. + +Code layout +----------- + +Custom template tags and filters must live inside a Django app. If they relate +to an existing app it makes sense to bundle them there; otherwise, you should +create a new app to hold them. + +The app should contain a ``templatetags`` directory, at the same level as +``models.py``, ``views.py``, etc. If this doesn't already exist, create it - +don't forget the ``__init__.py`` file to ensure the directory is treated as a +Python package. + +Your custom tags and filters will live in a module inside the ``templatetags`` +directory. The name of the module file is the name you'll use to load the tags +later, so be careful to pick a name that won't clash with custom tags and +filters in another app. + +For example, if your custom tags/filters are in a file called +``poll_extras.py``, your app layout might look like this:: + + polls/ + models.py + templatetags/ + __init__.py + poll_extras.py + views.py + +And in your template you would use the following: + +.. code-block:: html+django + + {% load poll_extras %} + +The app that contains the custom tags must be in :setting:`INSTALLED_APPS` in +order for the ``{% load %}`` tag to work. This is a security feature: It allows +you to host Python code for many template libraries on a single host machine +without enabling access to all of them for every Django installation. + +There's no limit on how many modules you put in the ``templatetags`` package. +Just keep in mind that a ``{% load %}`` statement will load tags/filters for +the given Python module name, not the name of the app. + +To be a valid tag library, the module must contain a module-level variable +named ``register`` that is a ``template.Library`` instance, in which all the +tags and filters are registered. So, near the top of your module, put the +following:: + + from django import template + + register = template.Library() + +.. admonition:: Behind the scenes + + For a ton of examples, read the source code for Django's default filters + and tags. They're in ``django/template/defaultfilters.py`` and + ``django/template/defaulttags.py``, respectively. + +Writing custom template filters +------------------------------- + +Custom filters are just Python functions that take one or two arguments: + + * The value of the variable (input) -- not necessarily a string. + * The value of the argument -- this can have a default value, or be left + out altogether. + +For example, in the filter ``{{ var|foo:"bar" }}``, the filter ``foo`` would be +passed the variable ``var`` and the argument ``"bar"``. + +Filter functions should always return something. They shouldn't raise +exceptions. They should fail silently. In case of error, they should return +either the original input or an empty string -- whichever makes more sense. + +Here's an example filter definition:: + + def cut(value, arg): + "Removes all values of arg from the given string" + return value.replace(arg, '') + +And here's an example of how that filter would be used: + +.. code-block:: html+django + + {{ somevariable|cut:"0" }} + +Most filters don't take arguments. In this case, just leave the argument out of +your function. Example:: + + def lower(value): # Only one argument. + "Converts a string into all lowercase" + return value.lower() + +Template filters that expect strings +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you're writing a template filter that only expects a string as the first +argument, you should use the decorator ``stringfilter``. This will +convert an object to its string value before being passed to your function:: + + from django.template.defaultfilters import stringfilter + + @stringfilter + def lower(value): + return value.lower() + +This way, you'll be able to pass, say, an integer to this filter, and it +won't cause an ``AttributeError`` (because integers don't have ``lower()`` +methods). + +Registering custom filters +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Once you've written your filter definition, you need to register it with +your ``Library`` instance, to make it available to Django's template language:: + + register.filter('cut', cut) + register.filter('lower', lower) + +The ``Library.filter()`` method takes two arguments: + + 1. The name of the filter -- a string. + 2. The compilation function -- a Python function (not the name of the + function as a string). + +You can use ``register.filter()`` as a decorator instead:: + + @register.filter(name='cut') + @stringfilter + def cut(value, arg): + return value.replace(arg, '') + + @register.filter + @stringfilter + def lower(value): + return value.lower() + +If you leave off the ``name`` argument, as in the second example above, Django +will use the function's name as the filter name. + +Filters and auto-escaping +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. versionadded:: 1.0 + +When writing a custom filter, give some thought to how the filter will interact +with Django's auto-escaping behavior. Note that three types of strings can be +passed around inside the template code: + + * **Raw strings** are the native Python ``str`` or ``unicode`` types. On + output, they're escaped if auto-escaping is in effect and presented + unchanged, otherwise. + + * **Safe strings** are strings that have been marked safe from further + escaping at output time. Any necessary escaping has already been done. + They're commonly used for output that contains raw HTML that is intended + to be interpreted as-is on the client side. + + Internally, these strings are of type ``SafeString`` or ``SafeUnicode``. + They share a common base class of ``SafeData``, so you can test + for them using code like:: + + if isinstance(value, SafeData): + # Do something with the "safe" string. + + * **Strings marked as "needing escaping"** are *always* escaped on + output, regardless of whether they are in an ``autoescape`` block or not. + These strings are only escaped once, however, even if auto-escaping + applies. + + Internally, these strings are of type ``EscapeString`` or + ``EscapeUnicode``. Generally you don't have to worry about these; they + exist for the implementation of the ``escape`` filter. + +Template filter code falls into one of two situations: + + 1. Your filter does not introduce any HTML-unsafe characters (``<``, ``>``, + ``'``, ``"`` or ``&``) into the result that were not already present. In + this case, you can let Django take care of all the auto-escaping + handling for you. All you need to do is put the ``is_safe`` attribute on + your filter function and set it to ``True``, like so:: + + @register.filter + def myfilter(value): + return value + myfilter.is_safe = True + + This attribute tells Django that if a "safe" string is passed into your + filter, the result will still be "safe" and if a non-safe string is + passed in, Django will automatically escape it, if necessary. + + You can think of this as meaning "this filter is safe -- it doesn't + introduce any possibility of unsafe HTML." + + The reason ``is_safe`` is necessary is because there are plenty of + normal string operations that will turn a ``SafeData`` object back into + a normal ``str`` or ``unicode`` object and, rather than try to catch + them all, which would be very difficult, Django repairs the damage after + the filter has completed. + + For example, suppose you have a filter that adds the string ``xx`` to the + end of any input. Since this introduces no dangerous HTML characters to + the result (aside from any that were already present), you should mark + your filter with ``is_safe``:: + + @register.filter + def add_xx(value): + return '%sxx' % value + add_xx.is_safe = True + + When this filter is used in a template where auto-escaping is enabled, + Django will escape the output whenever the input is not already marked as + "safe". + + By default, ``is_safe`` defaults to ``False``, and you can omit it from + any filters where it isn't required. + + Be careful when deciding if your filter really does leave safe strings + as safe. If you're *removing* characters, you might inadvertently leave + unbalanced HTML tags or entities in the result. For example, removing a + ``>`` from the input might turn ``<a>`` into ``<a``, which would need to + be escaped on output to avoid causing problems. Similarly, removing a + semicolon (``;``) can turn ``&`` into ``&``, which is no longer a + valid entity and thus needs further escaping. Most cases won't be nearly + this tricky, but keep an eye out for any problems like that when + reviewing your code. + + Marking a filter ``is_safe`` will coerce the filter's return value to + a string. If your filter should return a boolean or other non-string + value, marking it ``is_safe`` will probably have unintended + consequences (such as converting a boolean False to the string + 'False'). + + 2. Alternatively, your filter code can manually take care of any necessary + escaping. This is necessary when you're introducing new HTML markup into + the result. You want to mark the output as safe from further + escaping so that your HTML markup isn't escaped further, so you'll need + to handle the input yourself. + + To mark the output as a safe string, use + :func:`django.utils.safestring.mark_safe`. + + Be careful, though. You need to do more than just mark the output as + safe. You need to ensure it really *is* safe, and what you do depends on + whether auto-escaping is in effect. The idea is to write filters than + can operate in templates where auto-escaping is either on or off in + order to make things easier for your template authors. + + In order for your filter to know the current auto-escaping state, set + the ``needs_autoescape`` attribute to ``True`` on your function. (If you + don't specify this attribute, it defaults to ``False``). This attribute + tells Django that your filter function wants to be passed an extra + keyword argument, called ``autoescape``, that is ``True`` if + auto-escaping is in effect and ``False`` otherwise. + + For example, let's write a filter that emphasizes the first character of + a string:: + + from django.utils.html import conditional_escape + from django.utils.safestring import mark_safe + + def initial_letter_filter(text, autoescape=None): + first, other = text[0], text[1:] + if autoescape: + esc = conditional_escape + else: + esc = lambda x: x + result = '<strong>%s</strong>%s' % (esc(first), esc(other)) + return mark_safe(result) + initial_letter_filter.needs_autoescape = True + + The ``needs_autoescape`` attribute on the filter function and the + ``autoescape`` keyword argument mean that our function will know whether + automatic escaping is in effect when the filter is called. We use + ``autoescape`` to decide whether the input data needs to be passed + through ``django.utils.html.conditional_escape`` or not. (In the latter + case, we just use the identity function as the "escape" function.) The + ``conditional_escape()`` function is like ``escape()`` except it only + escapes input that is **not** a ``SafeData`` instance. If a ``SafeData`` + instance is passed to ``conditional_escape()``, the data is returned + unchanged. + + Finally, in the above example, we remember to mark the result as safe + so that our HTML is inserted directly into the template without further + escaping. + + There's no need to worry about the ``is_safe`` attribute in this case + (although including it wouldn't hurt anything). Whenever you manually + handle the auto-escaping issues and return a safe string, the + ``is_safe`` attribute won't change anything either way. + +Writing custom template tags +---------------------------- + +Tags are more complex than filters, because tags can do anything. + +A quick overview +~~~~~~~~~~~~~~~~ + +Above, this document explained that the template system works in a two-step +process: compiling and rendering. To define a custom template tag, you specify +how the compilation works and how the rendering works. + +When Django compiles a template, it splits the raw template text into +''nodes''. Each node is an instance of ``django.template.Node`` and has +a ``render()`` method. A compiled template is, simply, a list of ``Node`` +objects. When you call ``render()`` on a compiled template object, the template +calls ``render()`` on each ``Node`` in its node list, with the given context. +The results are all concatenated together to form the output of the template. + +Thus, to define a custom template tag, you specify how the raw template tag is +converted into a ``Node`` (the compilation function), and what the node's +``render()`` method does. + +Writing the compilation function +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For each template tag the template parser encounters, it calls a Python +function with the tag contents and the parser object itself. This function is +responsible for returning a ``Node`` instance based on the contents of the tag. + +For example, let's write a template tag, ``{% current_time %}``, that displays +the current date/time, formatted according to a parameter given in the tag, in +`strftime syntax`_. It's a good idea to decide the tag syntax before anything +else. In our case, let's say the tag should be used like this: + +.. code-block:: html+django + + <p>The time is {% current_time "%Y-%m-%d %I:%M %p" %}.</p> + +.. _`strftime syntax`: http://docs.python.org/library/time.html#time.strftime + +The parser for this function should grab the parameter and create a ``Node`` +object:: + + from django import template + def do_current_time(parser, token): + try: + # split_contents() knows not to split quoted strings. + tag_name, format_string = token.split_contents() + except ValueError: + raise template.TemplateSyntaxError, "%r tag requires a single argument" % token.contents.split()[0] + if not (format_string[0] == format_string[-1] and format_string[0] in ('"', "'")): + raise template.TemplateSyntaxError, "%r tag's argument should be in quotes" % tag_name + return CurrentTimeNode(format_string[1:-1]) + +Notes: + + * ``parser`` is the template parser object. We don't need it in this + example. + + * ``token.contents`` is a string of the raw contents of the tag. In our + example, it's ``'current_time "%Y-%m-%d %I:%M %p"'``. + + * The ``token.split_contents()`` method separates the arguments on spaces + while keeping quoted strings together. The more straightforward + ``token.contents.split()`` wouldn't be as robust, as it would naively + split on *all* spaces, including those within quoted strings. It's a good + idea to always use ``token.split_contents()``. + + * This function is responsible for raising + ``django.template.TemplateSyntaxError``, with helpful messages, for + any syntax error. + + * The ``TemplateSyntaxError`` exceptions use the ``tag_name`` variable. + Don't hard-code the tag's name in your error messages, because that + couples the tag's name to your function. ``token.contents.split()[0]`` + will ''always'' be the name of your tag -- even when the tag has no + arguments. + + * The function returns a ``CurrentTimeNode`` with everything the node needs + to know about this tag. In this case, it just passes the argument -- + ``"%Y-%m-%d %I:%M %p"``. The leading and trailing quotes from the + template tag are removed in ``format_string[1:-1]``. + + * The parsing is very low-level. The Django developers have experimented + with writing small frameworks on top of this parsing system, using + techniques such as EBNF grammars, but those experiments made the template + engine too slow. It's low-level because that's fastest. + +Writing the renderer +~~~~~~~~~~~~~~~~~~~~ + +The second step in writing custom tags is to define a ``Node`` subclass that +has a ``render()`` method. + +Continuing the above example, we need to define ``CurrentTimeNode``:: + + from django import template + import datetime + class CurrentTimeNode(template.Node): + def __init__(self, format_string): + self.format_string = format_string + def render(self, context): + return datetime.datetime.now().strftime(self.format_string) + +Notes: + + * ``__init__()`` gets the ``format_string`` from ``do_current_time()``. + Always pass any options/parameters/arguments to a ``Node`` via its + ``__init__()``. + + * The ``render()`` method is where the work actually happens. + + * ``render()`` should never raise ``TemplateSyntaxError`` or any other + exception. It should fail silently, just as template filters should. + +Ultimately, this decoupling of compilation and rendering results in an +efficient template system, because a template can render multiple contexts +without having to be parsed multiple times. + +Auto-escaping considerations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. versionadded:: 1.0 + +The output from template tags is **not** automatically run through the +auto-escaping filters. However, there are still a couple of things you should +keep in mind when writing a template tag. + +If the ``render()`` function of your template stores the result in a context +variable (rather than returning the result in a string), it should take care +to call ``mark_safe()`` if appropriate. When the variable is ultimately +rendered, it will be affected by the auto-escape setting in effect at the +time, so content that should be safe from further escaping needs to be marked +as such. + +Also, if your template tag creates a new context for performing some +sub-rendering, set the auto-escape attribute to the current context's value. +The ``__init__`` method for the ``Context`` class takes a parameter called +``autoescape`` that you can use for this purpose. For example:: + + def render(self, context): + # ... + new_context = Context({'var': obj}, autoescape=context.autoescape) + # ... Do something with new_context ... + +This is not a very common situation, but it's useful if you're rendering a +template yourself. For example:: + + def render(self, context): + t = template.loader.get_template('small_fragment.html') + return t.render(Context({'var': obj}, autoescape=context.autoescape)) + +If we had neglected to pass in the current ``context.autoescape`` value to our +new ``Context`` in this example, the results would have *always* been +automatically escaped, which may not be the desired behavior if the template +tag is used inside a ``{% autoescape off %}`` block. + +.. _template_tag_thread_safety: + +Thread-safety considerations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. versionadded:: 1.2 + +Once a node is parsed, its ``render`` method may be called any number of times. +Since Django is sometimes run in multi-threaded environments, a single node may +be simultaneously rendering with different contexts in response to two separate +requests. Therefore, it's important to make sure your template tags are thread +safe. + +To make sure your template tags are thread safe, you should never store state +information on the node itself. For example, Django provides a builtin ``cycle`` +template tag that cycles among a list of given strings each time it's rendered:: + + {% for o in some_list %} + <tr class="{% cycle 'row1' 'row2' %}> + ... + </tr> + {% endfor %} + +A naive implementation of ``CycleNode`` might look something like this:: + + class CycleNode(Node): + def __init__(self, cyclevars): + self.cycle_iter = itertools.cycle(cyclevars) + def render(self, context): + return self.cycle_iter.next() + +But, suppose we have two templates rendering the template snippet from above at +the same time: + + 1. Thread 1 performs its first loop iteration, ``CycleNode.render()`` + returns 'row1' + 2. Thread 2 performs its first loop iteration, ``CycleNode.render()`` + returns 'row2' + 3. Thread 1 performs its second loop iteration, ``CycleNode.render()`` + returns 'row1' + 4. Thread 2 performs its second loop iteration, ``CycleNode.render()`` + returns 'row2' + +The CycleNode is iterating, but it's iterating globally. As far as Thread 1 +and Thread 2 are concerned, it's always returning the same value. This is +obviously not what we want! + +To address this problem, Django provides a ``render_context`` that's associated +with the ``context`` of the template that is currently being rendered. The +``render_context`` behaves like a Python dictionary, and should be used to store +``Node`` state between invocations of the ``render`` method. + +Let's refactor our ``CycleNode`` implementation to use the ``render_context``:: + + class CycleNode(Node): + def __init__(self, cyclevars): + self.cyclevars = cyclevars + def render(self, context): + if self not in context.render_context: + context.render_context[self] = itertools.cycle(self.cyclevars) + cycle_iter = context.render_context[self] + return cycle_iter.next() + +Note that it's perfectly safe to store global information that will not change +throughout the life of the ``Node`` as an attribute. In the case of +``CycleNode``, the ``cyclevars`` argument doesn't change after the ``Node`` is +instantiated, so we don't need to put it in the ``render_context``. But state +information that is specific to the template that is currently being rendered, +like the current iteration of the ``CycleNode``, should be stored in the +``render_context``. + +.. note:: + Notice how we used ``self`` to scope the ``CycleNode`` specific information + within the ``render_context``. There may be multiple ``CycleNodes`` in a + given template, so we need to be careful not to clobber another node's state + information. The easiest way to do this is to always use ``self`` as the key + into ``render_context``. If you're keeping track of several state variables, + make ``render_context[self]`` a dictionary. + +Registering the tag +~~~~~~~~~~~~~~~~~~~ + +Finally, register the tag with your module's ``Library`` instance, as explained +in "Writing custom template filters" above. Example:: + + register.tag('current_time', do_current_time) + +The ``tag()`` method takes two arguments: + + 1. The name of the template tag -- a string. If this is left out, the + name of the compilation function will be used. + 2. The compilation function -- a Python function (not the name of the + function as a string). + +As with filter registration, it is also possible to use this as a decorator:: + + @register.tag(name="current_time") + def do_current_time(parser, token): + # ... + + @register.tag + def shout(parser, token): + # ... + +If you leave off the ``name`` argument, as in the second example above, Django +will use the function's name as the tag name. + +Passing template variables to the tag +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Although you can pass any number of arguments to a template tag using +``token.split_contents()``, the arguments are all unpacked as +string literals. A little more work is required in order to pass dynamic +content (a template variable) to a template tag as an argument. + +While the previous examples have formatted the current time into a string and +returned the string, suppose you wanted to pass in a ``DateTimeField`` from an +object and have the template tag format that date-time: + +.. code-block:: html+django + + <p>This post was last updated at {% format_time blog_entry.date_updated "%Y-%m-%d %I:%M %p" %}.</p> + +Initially, ``token.split_contents()`` will return three values: + + 1. The tag name ``format_time``. + 2. The string "blog_entry.date_updated" (without the surrounding quotes). + 3. The formatting string "%Y-%m-%d %I:%M %p". The return value from + ``split_contents()`` will include the leading and trailing quotes for + string literals like this. + +Now your tag should begin to look like this:: + + from django import template + def do_format_time(parser, token): + try: + # split_contents() knows not to split quoted strings. + tag_name, date_to_be_formatted, format_string = token.split_contents() + except ValueError: + raise template.TemplateSyntaxError, "%r tag requires exactly two arguments" % token.contents.split()[0] + if not (format_string[0] == format_string[-1] and format_string[0] in ('"', "'")): + raise template.TemplateSyntaxError, "%r tag's argument should be in quotes" % tag_name + return FormatTimeNode(date_to_be_formatted, format_string[1:-1]) + +.. versionchanged:: 1.0 + Variable resolution has changed in the 1.0 release of Django. ``template.resolve_variable()`` + has been deprecated in favor of a new ``template.Variable`` class. + +You also have to change the renderer to retrieve the actual contents of the +``date_updated`` property of the ``blog_entry`` object. This can be +accomplished by using the ``Variable()`` class in ``django.template``. + +To use the ``Variable`` class, simply instantiate it with the name of the +variable to be resolved, and then call ``variable.resolve(context)``. So, +for example:: + + class FormatTimeNode(template.Node): + def __init__(self, date_to_be_formatted, format_string): + self.date_to_be_formatted = template.Variable(date_to_be_formatted) + self.format_string = format_string + + def render(self, context): + try: + actual_date = self.date_to_be_formatted.resolve(context) + return actual_date.strftime(self.format_string) + except template.VariableDoesNotExist: + return '' + +Variable resolution will throw a ``VariableDoesNotExist`` exception if it cannot +resolve the string passed to it in the current context of the page. + +Shortcut for simple tags +~~~~~~~~~~~~~~~~~~~~~~~~ + +Many template tags take a number of arguments -- strings or a template variables +-- and return a string after doing some processing based solely on +the input argument and some external information. For example, the +``current_time`` tag we wrote above is of this variety: we give it a format +string, it returns the time as a string. + +To ease the creation of the types of tags, Django provides a helper function, +``simple_tag``. This function, which is a method of +``django.template.Library``, takes a function that accepts any number of +arguments, wraps it in a ``render`` function and the other necessary bits +mentioned above and registers it with the template system. + +Our earlier ``current_time`` function could thus be written like this:: + + def current_time(format_string): + return datetime.datetime.now().strftime(format_string) + + register.simple_tag(current_time) + +The decorator syntax also works:: + + @register.simple_tag + def current_time(format_string): + ... + +A couple of things to note about the ``simple_tag`` helper function: + + * Checking for the required number of arguments, etc., has already been + done by the time our function is called, so we don't need to do that. + * The quotes around the argument (if any) have already been stripped away, + so we just receive a plain string. + * If the argument was a template variable, our function is passed the + current value of the variable, not the variable itself. + +When your template tag does not need access to the current context, writing a +function to work with the input values and using the ``simple_tag`` helper is +the easiest way to create a new tag. + +.. _howto-custom-template-tags-inclusion-tags: + +Inclusion tags +~~~~~~~~~~~~~~ + +Another common type of template tag is the type that displays some data by +rendering *another* template. For example, Django's admin interface uses custom +template tags to display the buttons along the bottom of the "add/change" form +pages. Those buttons always look the same, but the link targets change depending +on the object being edited -- so they're a perfect case for using a small +template that is filled with details from the current object. (In the admin's +case, this is the ``submit_row`` tag.) + +These sorts of tags are called "inclusion tags". + +Writing inclusion tags is probably best demonstrated by example. Let's write a +tag that outputs a list of choices for a given ``Poll`` object, such as was +created in the :ref:`tutorials <creating-models>`. We'll use the tag like this: + +.. code-block:: html+django + + {% show_results poll %} + +...and the output will be something like this: + +.. code-block:: html + + <ul> + <li>First choice</li> + <li>Second choice</li> + <li>Third choice</li> + </ul> + +First, define the function that takes the argument and produces a dictionary of +data for the result. The important point here is we only need to return a +dictionary, not anything more complex. This will be used as a template context +for the template fragment. Example:: + + def show_results(poll): + choices = poll.choice_set.all() + return {'choices': choices} + +Next, create the template used to render the tag's output. This template is a +fixed feature of the tag: the tag writer specifies it, not the template +designer. Following our example, the template is very simple: + +.. code-block:: html+django + + <ul> + {% for choice in choices %} + <li> {{ choice }} </li> + {% endfor %} + </ul> + +Now, create and register the inclusion tag by calling the ``inclusion_tag()`` +method on a ``Library`` object. Following our example, if the above template is +in a file called ``results.html`` in a directory that's searched by the template +loader, we'd register the tag like this:: + + # Here, register is a django.template.Library instance, as before + register.inclusion_tag('results.html')(show_results) + +As always, decorator syntax works as well, so we could have written:: + + @register.inclusion_tag('results.html') + def show_results(poll): + ... + +...when first creating the function. + +Sometimes, your inclusion tags might require a large number of arguments, +making it a pain for template authors to pass in all the arguments and remember +their order. To solve this, Django provides a ``takes_context`` option for +inclusion tags. If you specify ``takes_context`` in creating a template tag, +the tag will have no required arguments, and the underlying Python function +will have one argument -- the template context as of when the tag was called. + +For example, say you're writing an inclusion tag that will always be used in a +context that contains ``home_link`` and ``home_title`` variables that point +back to the main page. Here's what the Python function would look like:: + + # The first argument *must* be called "context" here. + def jump_link(context): + return { + 'link': context['home_link'], + 'title': context['home_title'], + } + # Register the custom tag as an inclusion tag with takes_context=True. + register.inclusion_tag('link.html', takes_context=True)(jump_link) + +(Note that the first parameter to the function *must* be called ``context``.) + +In that ``register.inclusion_tag()`` line, we specified ``takes_context=True`` +and the name of the template. Here's what the template ``link.html`` might look +like: + +.. code-block:: html+django + + Jump directly to <a href="{{ link }}">{{ title }}</a>. + +Then, any time you want to use that custom tag, load its library and call it +without any arguments, like so: + +.. code-block:: html+django + + {% jump_link %} + +Note that when you're using ``takes_context=True``, there's no need to pass +arguments to the template tag. It automatically gets access to the context. + +The ``takes_context`` parameter defaults to ``False``. When it's set to *True*, +the tag is passed the context object, as in this example. That's the only +difference between this case and the previous ``inclusion_tag`` example. + +Setting a variable in the context +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The above examples simply output a value. Generally, it's more flexible if your +template tags set template variables instead of outputting values. That way, +template authors can reuse the values that your template tags create. + +To set a variable in the context, just use dictionary assignment on the context +object in the ``render()`` method. Here's an updated version of +``CurrentTimeNode`` that sets a template variable ``current_time`` instead of +outputting it:: + + class CurrentTimeNode2(template.Node): + def __init__(self, format_string): + self.format_string = format_string + def render(self, context): + context['current_time'] = datetime.datetime.now().strftime(self.format_string) + return '' + +Note that ``render()`` returns the empty string. ``render()`` should always +return string output. If all the template tag does is set a variable, +``render()`` should return the empty string. + +Here's how you'd use this new version of the tag: + +.. code-block:: html+django + + {% current_time "%Y-%M-%d %I:%M %p" %}<p>The time is {{ current_time }}.</p> + +.. admonition:: Variable scope in context + + Any variable set in the context will only be available in the same ``block`` + of the template in which it was assigned. This behaviour is intentional; + it provides a scope for variables so that they don't conflict with + context in other blocks. + +But, there's a problem with ``CurrentTimeNode2``: The variable name +``current_time`` is hard-coded. This means you'll need to make sure your +template doesn't use ``{{ current_time }}`` anywhere else, because the +``{% current_time %}`` will blindly overwrite that variable's value. A cleaner +solution is to make the template tag specify the name of the output variable, +like so: + +.. code-block:: html+django + + {% current_time "%Y-%M-%d %I:%M %p" as my_current_time %} + <p>The current time is {{ my_current_time }}.</p> + +To do that, you'll need to refactor both the compilation function and ``Node`` +class, like so:: + + class CurrentTimeNode3(template.Node): + def __init__(self, format_string, var_name): + self.format_string = format_string + self.var_name = var_name + def render(self, context): + context[self.var_name] = datetime.datetime.now().strftime(self.format_string) + return '' + + import re + def do_current_time(parser, token): + # This version uses a regular expression to parse tag contents. + try: + # Splitting by None == splitting by spaces. + tag_name, arg = token.contents.split(None, 1) + except ValueError: + raise template.TemplateSyntaxError, "%r tag requires arguments" % token.contents.split()[0] + m = re.search(r'(.*?) as (\w+)', arg) + if not m: + raise template.TemplateSyntaxError, "%r tag had invalid arguments" % tag_name + format_string, var_name = m.groups() + if not (format_string[0] == format_string[-1] and format_string[0] in ('"', "'")): + raise template.TemplateSyntaxError, "%r tag's argument should be in quotes" % tag_name + return CurrentTimeNode3(format_string[1:-1], var_name) + +The difference here is that ``do_current_time()`` grabs the format string and +the variable name, passing both to ``CurrentTimeNode3``. + +Parsing until another block tag +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Template tags can work in tandem. For instance, the standard ``{% comment %}`` +tag hides everything until ``{% endcomment %}``. To create a template tag such +as this, use ``parser.parse()`` in your compilation function. + +Here's how the standard ``{% comment %}`` tag is implemented:: + + def do_comment(parser, token): + nodelist = parser.parse(('endcomment',)) + parser.delete_first_token() + return CommentNode() + + class CommentNode(template.Node): + def render(self, context): + return '' + +``parser.parse()`` takes a tuple of names of block tags ''to parse until''. It +returns an instance of ``django.template.NodeList``, which is a list of +all ``Node`` objects that the parser encountered ''before'' it encountered +any of the tags named in the tuple. + +In ``"nodelist = parser.parse(('endcomment',))"`` in the above example, +``nodelist`` is a list of all nodes between the ``{% comment %}`` and +``{% endcomment %}``, not counting ``{% comment %}`` and ``{% endcomment %}`` +themselves. + +After ``parser.parse()`` is called, the parser hasn't yet "consumed" the +``{% endcomment %}`` tag, so the code needs to explicitly call +``parser.delete_first_token()``. + +``CommentNode.render()`` simply returns an empty string. Anything between +``{% comment %}`` and ``{% endcomment %}`` is ignored. + +Parsing until another block tag, and saving contents +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In the previous example, ``do_comment()`` discarded everything between +``{% comment %}`` and ``{% endcomment %}``. Instead of doing that, it's +possible to do something with the code between block tags. + +For example, here's a custom template tag, ``{% upper %}``, that capitalizes +everything between itself and ``{% endupper %}``. + +Usage: + +.. code-block:: html+django + + {% upper %}This will appear in uppercase, {{ your_name }}.{% endupper %} + +As in the previous example, we'll use ``parser.parse()``. But this time, we +pass the resulting ``nodelist`` to the ``Node``:: + + def do_upper(parser, token): + nodelist = parser.parse(('endupper',)) + parser.delete_first_token() + return UpperNode(nodelist) + + class UpperNode(template.Node): + def __init__(self, nodelist): + self.nodelist = nodelist + def render(self, context): + output = self.nodelist.render(context) + return output.upper() + +The only new concept here is the ``self.nodelist.render(context)`` in +``UpperNode.render()``. + +For more examples of complex rendering, see the source code for ``{% if %}``, +``{% for %}``, ``{% ifequal %}`` and ``{% ifchanged %}``. They live in +``django/template/defaulttags.py``. diff --git a/parts/django/docs/howto/deployment/fastcgi.txt b/parts/django/docs/howto/deployment/fastcgi.txt new file mode 100644 index 0000000..ea14b97 --- /dev/null +++ b/parts/django/docs/howto/deployment/fastcgi.txt @@ -0,0 +1,400 @@ +============================================ +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_. + +.. admonition:: Note + + This document primarily focuses on FastCGI. Other protocols, such as SCGI + and AJP, are also supported, through the ``flup`` Python package. See the + Protocols_ section below for specifics about SCGI and AJP. + +Essentially, FastCGI is an efficient way of letting an external application +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_python, FastCGI allows code to stay in memory, allowing requests to be +served with no startup time. Unlike mod_python_ (or `mod_perl`_), a FastCGI +process doesn't run inside the Web server process, but in a separate, +persistent process. + +.. _mod_wsgi: http://code.google.com/p/modwsgi/ +.. _mod_perl: http://perl.apache.org/ +.. _mod_python: http://www.modpython.org/ + +.. admonition:: Why run code in a separate process? + + The traditional ``mod_*`` arrangements in Apache embed various scripting + languages (most notably PHP, Python and Perl) inside the process space of + your Web server. Although this lowers startup time -- because code doesn't + have to be read off disk for every request -- it comes at the cost of + memory use. For mod_python, for example, every Apache process gets its own + Python interpreter, which uses up a considerable amount of RAM. + + Due to the nature of FastCGI, it's even possible to have processes that run + under a different user account than the Web server process. That's a nice + security benefit on shared systems, because it means you can secure your + code from other users. + +Prerequisite: flup +================== + +Before you can start using FastCGI with Django, you'll need to install flup_, a +Python library for dealing with FastCGI. Version 0.5 or newer should work fine. + +.. _flup: http://www.saddi.com/software/flup/ + +Starting your FastCGI server +============================ + +FastCGI operates on a client-server model, and in most cases you'll be starting +the FastCGI process on your own. Your Web server (be it Apache, lighttpd, or +otherwise) only contacts your Django-FastCGI process when the server needs a +dynamic page to be loaded. Because the daemon is already running with the code +in memory, it's able to serve the response very quickly. + +.. admonition:: Note + + If you're on a shared hosting system, you'll probably be forced to use + Web server-managed FastCGI processes. See the section below on running + Django with Web server-managed processes for more information. + +A Web server can connect to a FastCGI server in one of two ways: It can use +either a Unix domain socket (a "named pipe" on Win32 systems), or it can use a +TCP socket. What you choose is a manner of preference; a TCP socket is usually +easier due to permissions issues. + +To start your server, first change into the directory of your project (wherever +your :doc:`manage.py </ref/django-admin>` is), and then run the +:djadmin:`runfcgi` command:: + + ./manage.py runfcgi [options] + +If you specify ``help`` as the only option after :djadmin:`runfcgi`, it'll +display a list of all the available options. + +You'll need to specify either a :djadminopt:`socket`, a :djadminopt:`protocol` +or both :djadminopt:`host` and :djadminopt:`port`. Then, when you set up your +Web server, you'll just need to point it at the host/port or socket you +specified when starting the FastCGI server. See the examples_, below. + +Protocols +--------- + +Django supports all the protocols that flup_ does, namely fastcgi_, `SCGI`_ and +`AJP1.3`_ (the Apache JServ Protocol, version 1.3). Select your preferred +protocol by using the :djadminopt:`protocol=\<protocol_name\> <protocol>` option +with ``./manage.py runfcgi`` -- where ``<protocol_name>`` may be one of: +``fcgi`` (the default), ``scgi`` or ``ajp``. For example:: + + ./manage.py runfcgi protocol=scgi + +.. _flup: http://www.saddi.com/software/flup/ +.. _fastcgi: http://www.fastcgi.com/ +.. _SCGI: http://python.ca/scgi/protocol.txt +.. _AJP1.3: http://tomcat.apache.org/connectors-doc/ajp/ajpv13a.html + +Examples +-------- + +Running a threaded server on a TCP port:: + + ./manage.py runfcgi method=threaded host=127.0.0.1 port=3033 + +Running a preforked server on a Unix domain socket:: + + ./manage.py runfcgi method=prefork socket=/home/user/mysite.sock pidfile=django.pid + +.. admonition:: Socket security + + Django's default umask requires that the webserver and the Django fastcgi + process be run with the same group **and** user. For increased security, + you can run them under the same group but as different users. If you do + this, you will need to set the umask to 0002 using the ``umask`` argument + to ``runfcgi``. + +Run without daemonizing (backgrounding) the process (good for debugging):: + + ./manage.py runfcgi daemonize=false socket=/tmp/mysite.sock maxrequests=1 + +Stopping the FastCGI daemon +--------------------------- + +If you have the process running in the foreground, it's easy enough to stop it: +Simply hitting ``Ctrl-C`` will stop and quit the FastCGI server. However, when +you're dealing with background processes, you'll need to resort to the Unix +``kill`` command. + +If you specify the :djadminopt:`pidfile` option to :djadmin:`runfcgi`, you can +kill the running FastCGI daemon like this:: + + kill `cat $PIDFILE` + +...where ``$PIDFILE`` is the ``pidfile`` you specified. + +To easily restart your FastCGI daemon on Unix, try this small shell script:: + + #!/bin/bash + + # Replace these three settings. + PROJDIR="/home/user/myproject" + PIDFILE="$PROJDIR/mysite.pid" + SOCKET="$PROJDIR/mysite.sock" + + cd $PROJDIR + if [ -f $PIDFILE ]; then + kill `cat -- $PIDFILE` + rm -f -- $PIDFILE + fi + + exec /usr/bin/env - \ + PYTHONPATH="../python:.." \ + ./manage.py runfcgi socket=$SOCKET pidfile=$PIDFILE + +Apache setup +============ + +To use Django with Apache and FastCGI, you'll need Apache installed and +configured, with `mod_fastcgi`_ installed and enabled. Consult the Apache +documentation for instructions. + +Once you've got that set up, point Apache at your Django FastCGI instance by +editing the ``httpd.conf`` (Apache configuration) file. You'll need to do two +things: + + * Use the ``FastCGIExternalServer`` directive to specify the location of + your FastCGI server. + * Use ``mod_rewrite`` to point URLs at FastCGI as appropriate. + +.. _mod_fastcgi: http://www.fastcgi.com/mod_fastcgi/docs/mod_fastcgi.html + +Specifying the location of the FastCGI server +--------------------------------------------- + +The ``FastCGIExternalServer`` directive tells Apache how to find your FastCGI +server. As the `FastCGIExternalServer docs`_ explain, you can specify either a +``socket`` or a ``host``. Here are examples of both: + +.. code-block:: apache + + # Connect to FastCGI via a socket / named pipe. + FastCGIExternalServer /home/user/public_html/mysite.fcgi -socket /home/user/mysite.sock + + # Connect to FastCGI via a TCP host/port. + FastCGIExternalServer /home/user/public_html/mysite.fcgi -host 127.0.0.1:3033 + +In either case, the file ``/home/user/public_html/mysite.fcgi`` doesn't +actually have to exist. It's just a URL used by the Web server internally -- a +hook for signifying which requests at a URL should be handled by FastCGI. (More +on this in the next section.) + +.. _FastCGIExternalServer docs: http://www.fastcgi.com/mod_fastcgi/docs/mod_fastcgi.html#FastCgiExternalServer + +Using mod_rewrite to point URLs at FastCGI +------------------------------------------ + +The second step is telling Apache to use FastCGI for URLs that match a certain +pattern. To do this, use the `mod_rewrite`_ module and rewrite URLs to +``mysite.fcgi`` (or whatever you specified in the ``FastCGIExternalServer`` +directive, as explained in the previous section). + +In this example, we tell Apache to use FastCGI to handle any request that +doesn't represent a file on the filesystem and doesn't start with ``/media/``. +This is probably the most common case, if you're using Django's admin site: + +.. code-block:: apache + + <VirtualHost 12.34.56.78> + ServerName example.com + DocumentRoot /home/user/public_html + Alias /media /home/user/python/django/contrib/admin/media + RewriteEngine On + RewriteRule ^/(media.*)$ /$1 [QSA,L,PT] + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^/(.*)$ /mysite.fcgi/$1 [QSA,L] + </VirtualHost> + +.. _mod_rewrite: http://httpd.apache.org/docs/2.0/mod/mod_rewrite.html + +Django will automatically use the pre-rewrite version of the URL when +constructing URLs with the ``{% url %}`` template tag (and similar methods). + +lighttpd setup +============== + +lighttpd_ is a lightweight Web server commonly used for serving static files. It +supports FastCGI natively and, thus, is a good choice for serving both static +and dynamic pages, if your site doesn't have any Apache-specific needs. + +.. _lighttpd: http://www.lighttpd.net/ + +Make sure ``mod_fastcgi`` is in your modules list, somewhere after +``mod_rewrite`` and ``mod_access``, but not after ``mod_accesslog``. You'll +probably want ``mod_alias`` as well, for serving admin media. + +Add the following to your lighttpd config file: + +.. code-block:: lua + + server.document-root = "/home/user/public_html" + fastcgi.server = ( + "/mysite.fcgi" => ( + "main" => ( + # Use host / port instead of socket for TCP fastcgi + # "host" => "127.0.0.1", + # "port" => 3033, + "socket" => "/home/user/mysite.sock", + "check-local" => "disable", + ) + ), + ) + alias.url = ( + "/media" => "/home/user/django/contrib/admin/media/", + ) + + url.rewrite-once = ( + "^(/media.*)$" => "$1", + "^/favicon\.ico$" => "/media/favicon.ico", + "^(/.*)$" => "/mysite.fcgi$1", + ) + +Running multiple Django sites on one lighttpd +--------------------------------------------- + +lighttpd lets you use "conditional configuration" to allow configuration to be +customized per host. To specify multiple FastCGI sites, just add a conditional +block around your FastCGI config for each site:: + + # If the hostname is 'www.example1.com'... + $HTTP["host"] == "www.example1.com" { + server.document-root = "/foo/site1" + fastcgi.server = ( + ... + ) + ... + } + + # If the hostname is 'www.example2.com'... + $HTTP["host"] == "www.example2.com" { + server.document-root = "/foo/site2" + fastcgi.server = ( + ... + ) + ... + } + +You can also run multiple Django installations on the same site simply by +specifying multiple entries in the ``fastcgi.server`` directive. Add one +FastCGI host for each. + +Cherokee setup +============== + +Cherokee is a very fast, flexible and easy to configure Web Server. It +supports the widespread technologies nowadays: FastCGI, SCGI, PHP, CGI, SSI, +TLS and SSL encrypted connections, Virtual hosts, Authentication, on the fly +encoding, Load Balancing, Apache compatible log files, Data Base Balancer, +Reverse HTTP Proxy and much more. + +The Cherokee project provides a documentation to `setting up Django`_ with Cherokee. + +.. _setting up Django: http://www.cherokee-project.com/doc/cookbook_django.html + +Running Django on a shared-hosting provider with Apache +======================================================= + +Many shared-hosting providers don't allow you to run your own server daemons or +edit the ``httpd.conf`` file. In these cases, it's still possible to run Django +using Web server-spawned processes. + +.. admonition:: Note + + If you're using Web server-spawned processes, as explained in this section, + there's no need for you to start the FastCGI server on your own. Apache + will spawn a number of processes, scaling as it needs to. + +In your Web root directory, add this to a file named ``.htaccess``: + +.. code-block:: apache + + AddHandler fastcgi-script .fcgi + RewriteEngine On + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^(.*)$ mysite.fcgi/$1 [QSA,L] + +Then, create a small script that tells Apache how to spawn your FastCGI +program. Create a file ``mysite.fcgi`` and place it in your Web directory, and +be sure to make it executable: + +.. code-block:: python + + #!/usr/bin/python + import sys, os + + # Add a custom Python path. + sys.path.insert(0, "/home/user/python") + + # Switch to the directory of your project. (Optional.) + # os.chdir("/home/user/myproject") + + # Set the DJANGO_SETTINGS_MODULE environment variable. + os.environ['DJANGO_SETTINGS_MODULE'] = "myproject.settings" + + from django.core.servers.fastcgi import runfastcgi + runfastcgi(method="threaded", daemonize="false") + +Restarting the spawned server +----------------------------- + +If you change any Python code on your site, you'll need to tell FastCGI the +code has changed. But there's no need to restart Apache in this case. Rather, +just reupload ``mysite.fcgi``, or edit the file, so that the timestamp on the +file will change. When Apache sees the file has been updated, it will restart +your Django application for you. + +If you have access to a command shell on a Unix system, you can accomplish this +easily by using the ``touch`` command:: + + touch mysite.fcgi + +Serving admin media files +========================= + +Regardless of the server and configuration you eventually decide to use, you +will also need to give some thought to how to serve the admin media files. The +advice given in the :ref:`modpython <serving-the-admin-files>` documentation +is also applicable in the setups detailed above. + +Forcing the URL prefix to a particular value +============================================ + +Because many of these fastcgi-based solutions require rewriting the URL at +some point inside the Web server, the path information that Django sees may not +resemble the original URL that was passed in. This is a problem if the Django +application is being served from under a particular prefix and you want your +URLs from the ``{% url %}`` tag to look like the prefix, rather than the +rewritten version, which might contain, for example, ``mysite.fcgi``. + +Django makes a good attempt to work out what the real script name prefix +should be. In particular, if the Web server sets the ``SCRIPT_URL`` (specific +to Apache's mod_rewrite), or ``REDIRECT_URL`` (set by a few servers, including +Apache + mod_rewrite in some situations), Django will work out the original +prefix automatically. + +In the cases where Django cannot work out the prefix correctly and where you +want the original value to be used in URLs, you can set the +:setting:`FORCE_SCRIPT_NAME` setting in your main ``settings`` file. This sets the +script name uniformly for every URL served via that settings file. Thus you'll +need to use different settings files if you want different sets of URLs to +have different script names in this case, but that is a rare situation. + +As an example of how to use it, if your Django configuration is serving all of +the URLs under ``'/'`` and you wanted to use this setting, you would set +``FORCE_SCRIPT_NAME = ''`` in your settings file. diff --git a/parts/django/docs/howto/deployment/index.txt b/parts/django/docs/howto/deployment/index.txt new file mode 100644 index 0000000..740f9bc --- /dev/null +++ b/parts/django/docs/howto/deployment/index.txt @@ -0,0 +1,25 @@ +Deploying Django +================ + +Django's chock-full of shortcuts to make Web developer's lives easier, but all +those tools are of no use if you can't easily deploy your sites. Since Django's +inception, ease of deployment has been a major goal. There's a number of good +ways to easily deploy Django: + +.. toctree:: + :maxdepth: 1 + + modwsgi + modpython + fastcgi + +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 the easiest, +fastest, and most stable deployment choice. + +.. seealso:: + + * `Chapter 12 of The Django Book`_ discusses deployment and especially + scaling in more detail. + +.. _chapter 12 of the django book: http://djangobook.com/en/2.0/chapter12/ diff --git a/parts/django/docs/howto/deployment/modpython.txt b/parts/django/docs/howto/deployment/modpython.txt new file mode 100644 index 0000000..ba55335 --- /dev/null +++ b/parts/django/docs/howto/deployment/modpython.txt @@ -0,0 +1,418 @@ +.. _howto-deployment-modpython: + +============================================ +How to use Django with Apache and mod_python +============================================ + +.. 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>`. + +.. 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>`. + +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 +memory throughout the life of an Apache process, which leads to significant +performance gains over other server arrangements. + +Django requires Apache 2.x and mod_python 3.x, and you should use Apache's +`prefork MPM`_, as opposed to the `worker MPM`_. + +.. seealso:: + + * Apache is a big, complex animal, and this document only scratches the + surface of what Apache can do. If you need more advanced information about + Apache, there's no better source than `Apache's own official + documentation`_ + + * You may also be interested in :doc:`How to use Django with FastCGI, SCGI, + or AJP </howto/deployment/fastcgi>`. + +.. _Apache: http://httpd.apache.org/ +.. _mod_python: http://www.modpython.org/ +.. _mod_perl: http://perl.apache.org/ +.. _prefork MPM: http://httpd.apache.org/docs/2.2/mod/prefork.html +.. _worker MPM: http://httpd.apache.org/docs/2.2/mod/worker.html +.. _apache's own official documentation: http://httpd.apache.org/docs/ + +Basic configuration +=================== + +To configure Django with mod_python, first make sure you have Apache installed, +with the mod_python module activated. + +Then edit your ``httpd.conf`` file and add the following:: + + <Location "/mysite/"> + SetHandler python-program + PythonHandler django.core.handlers.modpython + SetEnv DJANGO_SETTINGS_MODULE mysite.settings + PythonOption django.root /mysite + PythonDebug On + </Location> + +...and replace ``mysite.settings`` with the Python import path to your Django +project's settings file. + +This tells Apache: "Use mod_python for any URL at or under '/mysite/', using the +Django mod_python handler." It passes the value of :ref:`DJANGO_SETTINGS_MODULE +<django-settings-module>` so mod_python knows which settings to use. + +.. versionadded:: 1.0 + The ``PythonOption django.root ...`` is new in this version. + +Because mod_python does not know we are serving this site from underneath the +``/mysite/`` prefix, this value needs to be passed through to the mod_python +handler in Django, via the ``PythonOption django.root ...`` line. The value set +on that line (the last item) should match the string given in the ``<Location +...>`` directive. The effect of this is that Django will automatically strip the +``/mysite`` string from the front of any URLs before matching them against your +URLconf patterns. If you later move your site to live under ``/mysite2``, you +will not have to change anything except the ``django.root`` option in the config +file. + +When using ``django.root`` you should make sure that what's left, after the +prefix has been removed, begins with a slash. Your URLconf patterns that are +expecting an initial slash will then work correctly. In the above example, +since we want to send things like ``/mysite/admin/`` to ``/admin/``, we need +to remove the string ``/mysite`` from the beginning, so that is the +``django.root`` value. It would be an error to use ``/mysite/`` (with a +trailing slash) in this case. + +Note that we're using the ``<Location>`` directive, not the ``<Directory>`` +directive. The latter is used for pointing at places on your filesystem, +whereas ``<Location>`` points at places in the URL structure of a Web site. +``<Directory>`` would be meaningless here. + +Also, if your Django project is not on the default ``PYTHONPATH`` for your +computer, you'll have to tell mod_python where your project can be found: + +.. parsed-literal:: + + <Location "/mysite/"> + SetHandler python-program + PythonHandler django.core.handlers.modpython + SetEnv DJANGO_SETTINGS_MODULE mysite.settings + PythonOption django.root /mysite + PythonDebug On + **PythonPath "['/path/to/project'] + sys.path"** + </Location> + +The value you use for ``PythonPath`` should include the parent directories of +all the modules you are going to import in your application. It should also +include the parent directory of the :ref:`DJANGO_SETTINGS_MODULE +<django-settings-module>` location. This is exactly the same situation as +setting the Python path for interactive usage. Whenever you try to import +something, Python will run through all the directories in ``sys.path`` in turn, +from first to last, and try to import from each directory until one succeeds. + +Make sure that your Python source files' permissions are set such that the +Apache user (usually named ``apache`` or ``httpd`` on most systems) will have +read access to the files. + +An example might make this clearer. Suppose you have some applications under +``/usr/local/django-apps/`` (for example, ``/usr/local/django-apps/weblog/`` and +so forth), your settings file is at ``/var/www/mysite/settings.py`` and you have +specified :ref:`DJANGO_SETTINGS_MODULE <django-settings-module>` as in the above +example. In this case, you would need to write your ``PythonPath`` directive +as:: + + PythonPath "['/usr/local/django-apps/', '/var/www'] + sys.path" + +With this path, ``import weblog`` and ``import mysite.settings`` will both +work. If you had ``import blogroll`` in your code somewhere and ``blogroll`` +lived under the ``weblog/`` directory, you would *also* need to add +``/usr/local/django-apps/weblog/`` to your ``PythonPath``. Remember: the +**parent directories** of anything you import directly must be on the Python +path. + +.. note:: + + If you're using Windows, we still recommended that you use forward + slashes in the pathnames, even though Windows normally uses the backslash + character as its native separator. Apache knows how to convert from the + forward slash format to the native format, so this approach is portable and + easier to read. (It avoids tricky problems with having to double-escape + backslashes.) + + This is valid even on a Windows system:: + + PythonPath "['c:/path/to/project'] + sys.path" + +You can also add directives such as ``PythonAutoReload Off`` for performance. +See the `mod_python documentation`_ for a full list of options. + +Note that you should set ``PythonDebug Off`` on a production server. If you +leave ``PythonDebug On``, your users would see ugly (and revealing) Python +tracebacks if something goes wrong within mod_python. + +Restart Apache, and any request to ``/mysite/`` or below will be served by +Django. Note that Django's URLconfs won't trim the "/mysite/" -- they get passed +the full URL. + +When deploying Django sites on mod_python, you'll need to restart Apache each +time you make changes to your Python code. + +.. _mod_python documentation: http://modpython.org/live/current/doc-html/directives.html + +Multiple Django installations on the same Apache +================================================ + +It's entirely possible to run multiple Django installations on the same Apache +instance. Just use ``VirtualHost`` for that, like so:: + + NameVirtualHost * + + <VirtualHost *> + ServerName www.example.com + # ... + SetEnv DJANGO_SETTINGS_MODULE mysite.settings + </VirtualHost> + + <VirtualHost *> + ServerName www2.example.com + # ... + SetEnv DJANGO_SETTINGS_MODULE mysite.other_settings + </VirtualHost> + +If you need to put two Django installations within the same ``VirtualHost`` +(or in different ``VirtualHost`` blocks that share the same server name), +you'll need to take a special precaution to ensure mod_python's cache doesn't +mess things up. Use the ``PythonInterpreter`` directive to give different +``<Location>`` directives separate interpreters:: + + <VirtualHost *> + ServerName www.example.com + # ... + <Location "/something"> + SetEnv DJANGO_SETTINGS_MODULE mysite.settings + PythonInterpreter mysite + </Location> + + <Location "/otherthing"> + SetEnv DJANGO_SETTINGS_MODULE mysite.other_settings + PythonInterpreter othersite + </Location> + </VirtualHost> + +The values of ``PythonInterpreter`` don't really matter, as long as they're +different between the two ``Location`` blocks. + +Running a development server with mod_python +============================================ + +If you use mod_python for your development server, you can avoid the hassle of +having to restart the server each time you make code changes. Just set +``MaxRequestsPerChild 1`` in your ``httpd.conf`` file to force Apache to reload +everything for each request. But don't do that on a production server, or we'll +revoke your Django privileges. + +If you're the type of programmer who debugs using scattered ``print`` +statements, note that output to ``stdout`` will not appear in the Apache +log and can even `cause response errors`_. + +.. _cause response errors: http://blog.dscpl.com.au/2009/04/wsgi-and-printing-to-standard-output.html + +If you have the need to print debugging information in a mod_python setup, you +have a few options. You can print to ``stderr`` explicitly, like so:: + + print >> sys.stderr, 'debug text' + sys.stderr.flush() + +(note that ``stderr`` is buffered, so calling ``flush`` is necessary if you wish +debugging information to be displayed promptly.) + +A more compact approach is to use an assertion:: + + assert False, 'debug text' + +Another alternative is to add debugging information to the template of your page. + +.. _serving-media-files: + +Serving media files +=================== + +Django doesn't serve media 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, here's how you can turn off mod_python for a +particular part of the site:: + + <Location "/media"> + SetHandler None + </Location> + +Just change ``Location`` to the root URL of your media files. You can also use +``<LocationMatch>`` to match a regular expression. + +This example sets up Django at the site root but explicitly disables Django for +the ``media`` subdirectory and any URL that ends with ``.jpg``, ``.gif`` or +``.png``:: + + <Location "/"> + SetHandler python-program + PythonHandler django.core.handlers.modpython + SetEnv DJANGO_SETTINGS_MODULE mysite.settings + </Location> + + <Location "/media"> + SetHandler None + </Location> + + <LocationMatch "\.(jpg|gif|png)$"> + SetHandler None + </LocationMatch> + + +.. _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/ + +.. _serving-the-admin-files: + +Serving the admin files +======================= + +Note that the Django development server automagically serves admin media files, +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/media`) of the Django +distribution. + +Here are two recommended approaches: + + 1. Create a symbolic link to the admin media files from within your + document root. This way, all of your Django-related files -- code **and** + templates -- stay in one place, and you'll still be able to ``svn + update`` your code to get the latest admin templates, if they change. + + 2. Or, copy the admin media files so that they live within your Apache + document root. + +Using "eggs" with mod_python +============================ + +If you installed Django from a Python egg_ or are using eggs in your Django +project, some extra configuration is required. Create an extra file in your +project (or somewhere else) that contains something like the following: + +.. code-block:: python + + import os + os.environ['PYTHON_EGG_CACHE'] = '/some/directory' + +Here, ``/some/directory`` is a directory that the Apache Web server process can +write to. It will be used as the location for any unpacking of code the eggs +need to do. + +Then you have to tell mod_python to import this file before doing anything +else. This is done using the PythonImport_ directive to mod_python. You need +to ensure that you have specified the ``PythonInterpreter`` directive to +mod_python as described above__ (you need to do this even if you aren't +serving multiple installations in this case). Then add the ``PythonImport`` +line in the main server configuration (i.e., outside the ``Location`` or +``VirtualHost`` sections). For example:: + + PythonInterpreter my_django + PythonImport /path/to/my/project/file.py my_django + +Note that you can use an absolute path here (or a normal dotted import path), +as described in the `mod_python manual`_. We use an absolute path in the +above example because if any Python path modifications are required to access +your project, they will not have been done at the time the ``PythonImport`` +line is processed. + +.. _Egg: http://peak.telecommunity.com/DevCenter/PythonEggs +.. _PythonImport: http://www.modpython.org/live/current/doc-html/dir-other-pimp.html +.. _mod_python manual: PythonImport_ +__ `Multiple Django installations on the same Apache`_ + +Error handling +============== + +When you use Apache/mod_python, errors will be caught by Django -- in other +words, they won't propagate to the Apache level and won't appear in the Apache +``error_log``. + +The exception for this is if something is really wonky in your Django setup. In +that case, you'll see an "Internal Server Error" page in your browser and the +full Python traceback in your Apache ``error_log`` file. The ``error_log`` +traceback is spread over multiple lines. (Yes, this is ugly and rather hard to +read, but it's how mod_python does things.) + +If you get a segmentation fault +=============================== + +If Apache causes a segmentation fault, there are two probable causes, neither +of which has to do with Django itself. + + 1. It may be because your Python code is importing the "pyexpat" module, + which may conflict with the version embedded in Apache. For full + information, see `Expat Causing Apache Crash`_. + + 2. It may be because you're running mod_python and mod_php in the same + Apache instance, with MySQL as your database backend. In some cases, + this causes a known mod_python issue due to version conflicts in PHP and + the Python MySQL backend. There's full information in the + `mod_python FAQ entry`_. + +If you continue to have problems setting up mod_python, a good thing to do is +get a barebones mod_python site working, without the Django framework. This is +an easy way to isolate mod_python-specific problems. `Getting mod_python Working`_ +details this procedure. + +The next step should be to edit your test code and add an import of any +Django-specific code you're using -- your views, your models, your URLconf, +your RSS configuration, etc. Put these imports in your test handler function +and access your test URL in a browser. If this causes a crash, you've confirmed +it's the importing of Django code that causes the problem. Gradually reduce the +set of imports until it stops crashing, so as to find the specific module that +causes the problem. Drop down further into modules and look into their imports, +as necessary. + +.. _Expat Causing Apache Crash: http://www.dscpl.com.au/wiki/ModPython/Articles/ExpatCausingApacheCrash +.. _mod_python FAQ entry: http://modpython.org/FAQ/faqw.py?req=show&file=faq02.013.htp +.. _Getting mod_python Working: http://www.dscpl.com.au/wiki/ModPython/Articles/GettingModPythonWorking + +If you get a UnicodeEncodeError +=============================== + +If you're taking advantage of the internationalization features of Django (see +:doc:`/topics/i18n/index`) and you intend to allow users to upload files, you must +ensure that the environment used to start Apache is configured to accept +non-ASCII file names. If your environment is not correctly configured, you +will trigger ``UnicodeEncodeError`` exceptions when calling functions like +``os.path()`` on filenames that contain non-ASCII characters. + +To avoid these problems, the environment used to start Apache should contain +settings analogous to the following:: + + export LANG='en_US.UTF-8' + export LC_ALL='en_US.UTF-8' + +Consult the documentation for your operating system for the appropriate syntax +and location to put these configuration items; ``/etc/apache2/envvars`` is a +common location on Unix platforms. Once you have added these statements +to your environment, restart Apache. diff --git a/parts/django/docs/howto/deployment/modwsgi.txt b/parts/django/docs/howto/deployment/modwsgi.txt new file mode 100644 index 0000000..17ba0e3 --- /dev/null +++ b/parts/django/docs/howto/deployment/modwsgi.txt @@ -0,0 +1,118 @@ +========================================== +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`_, including Django. Django will work +with any version of Apache which supports mod_wsgi. + +.. _python wsgi interface: http://www.python.org/dev/peps/pep-0333/ + +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 = '/usr/local/django' + if path not in sys.path: + sys.path.append(path) + +just above the final ``import`` line to place your project on the path. Remember to +replace 'mysite.settings' with your correct settings file, and '/usr/local/django' +with your own project's location. + +Serving media files +=================== + +Django doesn't serve media 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 ``/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/static/media/ + + <Directory /usr/local/wsgi/static> + 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 + +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/parts/django/docs/howto/error-reporting.txt b/parts/django/docs/howto/error-reporting.txt new file mode 100644 index 0000000..9c61c97 --- /dev/null +++ b/parts/django/docs/howto/error-reporting.txt @@ -0,0 +1,78 @@ +Error reporting via e-mail +========================== + +When you're running a public site you should always turn off the +:setting:`DEBUG` setting. That will make your server run much faster, and will +also prevent malicious users from seeing details of your application that can be +revealed by the error pages. + +However, running with :setting:`DEBUG` set to ``False`` means you'll never see +errors generated by your site -- everyone will just see your public error pages. +You need to keep track of errors that occur in deployed sites, so Django can be +configured to e-mail you details of those errors. + +Server errors +------------- + +When :setting:`DEBUG` is ``False``, Django will e-mail the users listed in the +:setting:`ADMINS` setting whenever your code raises an unhandled exception and +results in an internal server error (HTTP status code 500). This gives the +administrators immediate notification of any errors. The :setting:`ADMINS` will +get a description of the error, a complete Python traceback, and details about +the HTTP request that caused the error. + +.. note:: + + In order to send e-mail, Django requires a few settings telling it + how to connect to your mail server. At the very least, you'll need + to specify :setting:`EMAIL_HOST` and possibly + :setting:`EMAIL_HOST_USER` and :setting:`EMAIL_HOST_PASSWORD`, + though other settings may be also required depending on your mail + server's configuration. Consult :doc:`the Django settings + documentation </ref/settings>` for a full list of email-related + settings. + +By default, Django will send e-mail from root@localhost. However, some mail +providers reject all e-mail from this address. To use a different sender +address, modify the :setting:`SERVER_EMAIL` setting. + +To disable this behavior, just remove all entries from the :setting:`ADMINS` +setting. + +404 errors +---------- + +Django can also be configured to e-mail errors about broken links (404 "page +not found" errors). Django sends e-mails about 404 errors when: + + * :setting:`DEBUG` is ``False`` + + * :setting:`SEND_BROKEN_LINK_EMAILS` is ``True`` + + * Your :setting:`MIDDLEWARE_CLASSES` setting includes ``CommonMiddleware`` + (which it does by default). + +If those conditions are met, Django will e-mail the users listed in the +:setting:`MANAGERS` setting whenever your code raises a 404 and the request has +a referer. (It doesn't bother to e-mail for 404s that don't have a referer -- +those are usually just people typing in broken URLs or broken Web 'bots). + +You can tell Django to stop reporting particular 404s by tweaking the +:setting:`IGNORABLE_404_ENDS` and :setting:`IGNORABLE_404_STARTS` settings. Both +should be a tuple of strings. For example:: + + IGNORABLE_404_ENDS = ('.php', '.cgi') + IGNORABLE_404_STARTS = ('/phpmyadmin/',) + +In this example, a 404 to any URL ending with ``.php`` or ``.cgi`` will *not* be +reported. Neither will any URL starting with ``/phpmyadmin/``. + +The best way to disable this behavior is to set +:setting:`SEND_BROKEN_LINK_EMAILS` to ``False``. + +.. seealso:: + + You can also set up custom error reporting by writing a custom piece of + :ref:`exception middleware <exception-middleware>`. If you do write custom + error handling, it's a good idea to emulate Django's built-in error handling + and only report/log errors if :setting:`DEBUG` is ``False``. diff --git a/parts/django/docs/howto/i18n.txt b/parts/django/docs/howto/i18n.txt new file mode 100644 index 0000000..64b33d7 --- /dev/null +++ b/parts/django/docs/howto/i18n.txt @@ -0,0 +1,103 @@ +.. _using-translations-in-your-own-projects: + +=============================================== +Using internationalization in your own projects +=============================================== + +At runtime, Django looks for translations by following this algorithm: + + * First, it looks for a ``locale`` directory in the directory containing + your settings file. + * Second, it looks for a ``locale`` directory in the project directory. + * Third, it looks for a ``locale`` directory in each of the installed apps. + It does this in the reverse order of INSTALLED_APPS + * Finally, it checks the Django-provided base translation in + ``django/conf/locale``. + +In all cases the name of the directory containing the translation is expected to +be named using :term:`locale name` notation. E.g. ``de``, ``pt_BR``, ``es_AR``, +etc. + +This way, you can write applications that include their own translations, and +you can override base translations in your project path. Or, you can just build +a big project out of several apps and put all translations into one big project +message file. The choice is yours. + +.. note:: + + If you're using manually configured settings, as described in + :ref:`settings-without-django-settings-module`, the ``locale`` directory in + the project directory will not be examined, since Django loses the ability + to work out the location of the project directory. (Django normally uses the + location of the settings file to determine this, and a settings file doesn't + exist if you're manually configuring your settings.) + +All message file repositories are structured the same way. They are: + + * ``$APPPATH/locale/<language>/LC_MESSAGES/django.(po|mo)`` + * ``$PROJECTPATH/locale/<language>/LC_MESSAGES/django.(po|mo)`` + * All paths listed in ``LOCALE_PATHS`` in your settings file are + searched in that order for ``<language>/LC_MESSAGES/django.(po|mo)`` + * ``$PYTHONPATH/django/conf/locale/<language>/LC_MESSAGES/django.(po|mo)`` + +To create message files, you use the :djadmin:`django-admin.py makemessages <makemessages>` +tool. You only need to be in the same directory where the ``locale/`` directory +is located. And you use :djadmin:`django-admin.py compilemessages <compilemessages>` +to produce the binary ``.mo`` files that are used by ``gettext``. Read the +:doc:`/topics/i18n/localization` document for more details. + +You can also run ``django-admin.py compilemessages --settings=path.to.settings`` +to make the compiler process all the directories in your :setting:`LOCALE_PATHS` +setting. + +Application message files are a bit complicated to discover -- they need the +:class:`~django.middleware.locale.LocaleMiddleware`. If you don't use the +middleware, only the Django message files and project message files will be +installed and available at runtime. + +Finally, you should give some thought to the structure of your translation +files. If your applications need to be delivered to other users and will +be used in other projects, you might want to use app-specific translations. +But using app-specific translations and project translations could produce +weird problems with ``makemessages``: It will traverse all directories below +the current path and so might put message IDs into the project message file +that are already in application message files. + +The easiest way out is to store applications that are not part of the project +(and so carry their own translations) outside the project tree. That way, +``django-admin.py makemessages`` on the project level will only translate +strings that are connected to your explicit project and not strings that are +distributed independently. + +Using translations outside views and templates +============================================== + +While Django provides a rich set of i18n tools for use in views and templates, +it does not restrict the usage to Django-specific code. The Django translation +mechanisms can be used to translate arbitrary texts to any language that is +supported by Django (as long as an appropriate translation catalog exists, of +course). You can load a translation catalog, activate it and translate text to +language of your choice, but remember to switch back to original language, as +activating a translation catalog is done on per-thread basis and such change +will affect code running in the same thread. + +For example:: + + from django.utils import translation + def welcome_translated(language): + cur_language = translation.get_language() + try: + translation.activate(language) + text = translation.ugettext('welcome') + finally: + translation.activate(cur_language) + return text + +Calling this function with the value 'de' will give you ``"Willkommen"``, +regardless of :setting:`LANGUAGE_CODE` and language set by middleware. + +Functions of particular interest are ``django.utils.translation.get_language()`` +which returns the language used in the current thread, +``django.utils.translation.activate()`` which activates a translation catalog +for the current thread, and ``django.utils.translation.check_for_language()`` +which checks if the given language is supported by Django. diff --git a/parts/django/docs/howto/index.txt b/parts/django/docs/howto/index.txt new file mode 100644 index 0000000..49d0644 --- /dev/null +++ b/parts/django/docs/howto/index.txt @@ -0,0 +1,34 @@ +"How-to" guides +=============== + +Here you'll find short answers to "How do I....?" types of questions. These +how-to guides don't cover topics in depth -- you'll find that material in the +:doc:`/topics/index` and the :doc:`/ref/index`. However, these guides will help +you quickly accomplish common tasks. + +.. toctree:: + :maxdepth: 1 + + apache-auth + auth-remote-user + custom-management-commands + custom-model-fields + custom-template-tags + custom-file-storage + deployment/index + error-reporting + initial-data + i18n + jython + legacy-databases + outputting-csv + outputting-pdf + static-files + +.. seealso:: + + The `Django community aggregator`_, where we aggregate content from the + global Django community. Many writers in the aggregator write this sort of + how-to material. + + .. _django community aggregator: http://www.djangoproject.com/community/ diff --git a/parts/django/docs/howto/initial-data.txt b/parts/django/docs/howto/initial-data.txt new file mode 100644 index 0000000..cf3f65d --- /dev/null +++ b/parts/django/docs/howto/initial-data.txt @@ -0,0 +1,142 @@ +================================= +Providing initial data for models +================================= + +It's sometimes useful to pre-populate your database with hard-coded data when +you're first setting up an app. There's a couple of ways you can have Django +automatically create this data: you can provide `initial data via fixtures`_, or +you can provide `initial data as SQL`_. + +In general, using a fixture is a cleaner method since it's database-agnostic, +but initial SQL is also quite a bit more flexible. + +.. _initial data as sql: `providing initial sql data`_ +.. _initial data via fixtures: `providing initial data with fixtures`_ + +Providing initial data with fixtures +==================================== + +A fixture is a collection of data that Django knows how to import into a +database. The most straightforward way of creating a fixture if you've already +got some data is to use the :djadmin:`manage.py dumpdata <dumpdata>` command. +Or, you can write fixtures by hand; fixtures can be written as XML, YAML, or +JSON documents. The :doc:`serialization documentation </topics/serialization>` +has more details about each of these supported :ref:`serialization formats +<serialization-formats>`. + +As an example, though, here's what a fixture for a simple ``Person`` model might +look like in JSON: + +.. code-block:: js + + [ + { + "model": "myapp.person", + "pk": 1, + "fields": { + "first_name": "John", + "last_name": "Lennon" + } + }, + { + "model": "myapp.person", + "pk": 2, + "fields": { + "first_name": "Paul", + "last_name": "McCartney" + } + } + ] + +And here's that same fixture as YAML: + +.. code-block:: none + + - model: myapp.person + pk: 1 + fields: + first_name: John + last_name: Lennon + - model: myapp.person + pk: 2 + fields: + first_name: Paul + last_name: McCartney + +You'll store this data in a ``fixtures`` directory inside your app. + +Loading data is easy: just call :djadmin:`manage.py loaddata fixturename +<loaddata>`, where *fixturename* is the name of the fixture file you've created. +Every time you run :djadmin:`loaddata` the data will be read from the fixture +and re-loaded into the database. Note that this means that if you change one of +the rows created by a fixture and then run :djadmin:`loaddata` again you'll +wipe out any changes you've made. + +Automatically loading initial data fixtures +------------------------------------------- + +If you create a fixture named ``initial_data.[xml/yaml/json]``, that fixture will +be loaded every time you run :djadmin:`syncdb`. This is extremely convenient, +but be careful: remember that the data will be refreshed *every time* you run +:djadmin:`syncdb`. So don't use ``initial_data`` for data you'll want to edit. + +.. seealso:: + + Fixtures are also used by the :ref:`testing framework + <topics-testing-fixtures>` to help set up a consistent test environment. + +.. _initial-sql: + +Providing initial SQL data +========================== + +Django provides a hook for passing the database arbitrary SQL that's executed +just after the CREATE TABLE statements when you run :djadmin:`syncdb`. You can +use this hook to populate default records, or you could also create SQL +functions, views, triggers, etc. + +The hook is simple: Django just looks for a file called ``sql/<modelname>.sql``, +in your app directory, where ``<modelname>`` is the model's name in lowercase. + +So, if you had a ``Person`` model in an app called ``myapp``, you could add +arbitrary SQL to the file ``sql/person.sql`` inside your ``myapp`` directory. +Here's an example of what the file might contain: + +.. code-block:: sql + + INSERT INTO myapp_person (first_name, last_name) VALUES ('John', 'Lennon'); + INSERT INTO myapp_person (first_name, last_name) VALUES ('Paul', 'McCartney'); + +Each SQL file, if given, is expected to contain valid SQL statements +which will insert the desired data (e.g., properly-formatted +``INSERT`` statements separated by semicolons). + +The SQL files are read by the :djadmin:`sqlcustom`, :djadmin:`sqlreset`, +:djadmin:`sqlall` and :djadmin:`reset` commands in :doc:`manage.py +</ref/django-admin>`. Refer to the :doc:`manage.py documentation +</ref/django-admin>` for more information. + +Note that if you have multiple SQL data files, there's no guarantee of +the order in which they're executed. The only thing you can assume is +that, by the time your custom data files are executed, all the +database tables already will have been created. + +Database-backend-specific SQL data +---------------------------------- + +There's also a hook for backend-specific SQL data. For example, you +can have separate initial-data files for PostgreSQL and MySQL. For +each app, Django looks for a file called +``<appname>/sql/<modelname>.<backend>.sql``, where ``<appname>`` is +your app directory, ``<modelname>`` is the model's name in lowercase +and ``<backend>`` is the last part of the module name provided for the +:setting:`ENGINE` in your settings file (e.g., if you have defined a +database with an :setting:`ENGINE` value of +``django.db.backends.postgresql``, Django will look for +``<appname>/sql/<modelname>.postgresql.sql``). + +Backend-specific SQL data is executed before non-backend-specific SQL +data. For example, if your app contains the files ``sql/person.sql`` +and ``sql/person.postgresql.sql`` and you're installing the app on +PostgreSQL, Django will execute the contents of +``sql/person.postgresql.sql`` first, then ``sql/person.sql``. diff --git a/parts/django/docs/howto/jython.txt b/parts/django/docs/howto/jython.txt new file mode 100644 index 0000000..1bf8d6c --- /dev/null +++ b/parts/django/docs/howto/jython.txt @@ -0,0 +1,73 @@ +======================== +Running Django on Jython +======================== + +.. index:: Jython, Java, JVM + +Jython_ is an implementation of Python that runs on the Java platform (JVM). +Django runs cleanly on Jython version 2.5 or later, which means you can deploy +Django on any Java platform. + +This document will get you up and running with Django on top of Jython. + +.. _jython: http://www.jython.org/ + +Installing Jython +================= + +Django works with Jython versions 2.5b3 and higher. Download Jython at +http://www.jython.org/. + +Creating a servlet container +============================ + +If you just want to experiment with Django, skip ahead to the next section; +Django includes a lightweight Web server you can use for testing, so you won't +need to set up anything else until you're ready to deploy Django in production. + +If you want to use Django on a production site, use a Java servlet container, +such as `Apache Tomcat`_. Full JavaEE applications servers such as `GlassFish`_ +or `JBoss`_ are also OK, if you need the extra features they include. + +.. _`Apache Tomcat`: http://tomcat.apache.org/ +.. _GlassFish: https://glassfish.dev.java.net/ +.. _JBoss: http://www.jboss.org/ + +Installing Django +================= + +The next step is to install Django itself. This is exactly the same as +installing Django on standard Python, so see +:ref:`removing-old-versions-of-django` and :ref:`install-django-code` for +instructions. + +Installing Jython platform support libraries +============================================ + +The `django-jython`_ project contains database backends and management commands +for Django/Jython development. Note that the builtin Django backends won't work +on top of Jython. + +.. _`django-jython`: http://code.google.com/p/django-jython/ + +To install it, follow the `installation instructions`_ detailed on the project +Web site. Also, read the `database backends`_ documentation there. + +.. _`installation instructions`: http://code.google.com/p/django-jython/wiki/Install +.. _`database backends`: http://code.google.com/p/django-jython/wiki/DatabaseBackends + +Differences with Django on Jython +================================= + +.. index:: JYTHONPATH + +At this point, Django on Jython should behave nearly identically to Django +running on standard Python. However, are a few differences to keep in mind: + + * Remember to use the ``jython`` command instead of ``python``. The + documentation uses ``python`` for consistancy, but if you're using Jython + you'll want to mentally replace ``python`` with ``jython`` every time it + occurs. + + * Similarly, you'll need to use the ``JYTHONPATH`` environment variable + instead of ``PYTHONPATH``. diff --git a/parts/django/docs/howto/legacy-databases.txt b/parts/django/docs/howto/legacy-databases.txt new file mode 100644 index 0000000..2121871 --- /dev/null +++ b/parts/django/docs/howto/legacy-databases.txt @@ -0,0 +1,66 @@ +========================================= +Integrating Django with a legacy database +========================================= + +While Django is best suited for developing new applications, it's quite +possible to integrate it into legacy databases. Django includes a couple of +utilities to automate as much of this process as possible. + +This document assumes you know the Django basics, as covered in the +:doc:`tutorial </intro/tutorial01>`. + +Once you've got Django set up, you'll follow this general process to integrate +with an existing database. + +Give Django your database parameters +==================================== + +You'll need to tell Django what your database connection parameters are, and +what the name of the database is. Do that by editing the :setting:`DATABASES` +setting and assigning values to the following keys for the ``'default'`` +connection: + + * :setting:`NAME` + * :setting:`ENGINE` + * :setting:`USER` + * :setting:`PASSWORD` + * :setting:`HOST` + * :setting:`PORT` + +Auto-generate the models +======================== + +.. highlight:: bash + +Django comes with a utility called :djadmin:`inspectdb` that can create models +by introspecting an existing database. You can view the output by running this +command:: + + python manage.py inspectdb + +Save this as a file by using standard Unix output redirection:: + + python manage.py inspectdb > models.py + +This feature is meant as a shortcut, not as definitive model generation. See the +:djadmin:`documentation of inspectdb <inspectdb>` for more information. + +Once you've cleaned up your models, name the file ``models.py`` and put it in +the Python package that holds your app. Then add the app to your +:setting:`INSTALLED_APPS` setting. + +Install the core Django tables +============================== + +Next, run the :djadmin:`syncdb` command to install any extra needed database +records such as admin permissions and content types:: + + python manage.py syncdb + +Test and tweak +============== + +Those are the basic steps -- from here you'll want to tweak the models Django +generated until they work the way you'd like. Try accessing your data via the +Django database API, and try editing objects via Django's admin site, and edit +the models file accordingly. diff --git a/parts/django/docs/howto/outputting-csv.txt b/parts/django/docs/howto/outputting-csv.txt new file mode 100644 index 0000000..46e111d --- /dev/null +++ b/parts/django/docs/howto/outputting-csv.txt @@ -0,0 +1,137 @@ +========================== +Outputting CSV with Django +========================== + +This document explains how to output CSV (Comma Separated Values) dynamically +using Django views. To do this, you can either use the `Python CSV library`_ or +the Django template system. + +.. _Python CSV library: http://docs.python.org/library/csv.html + +Using the Python CSV library +============================ + +Python comes with a CSV library, ``csv``. The key to using it with Django is +that the ``csv`` module's CSV-creation capability acts on file-like objects, and +Django's :class:`~django.http.HttpResponse` objects are file-like objects. + +Here's an example:: + + import csv + from django.http import HttpResponse + + def some_view(request): + # Create the HttpResponse object with the appropriate CSV header. + response = HttpResponse(mimetype='text/csv') + response['Content-Disposition'] = 'attachment; filename=somefilename.csv' + + writer = csv.writer(response) + writer.writerow(['First row', 'Foo', 'Bar', 'Baz']) + writer.writerow(['Second row', 'A', 'B', 'C', '"Testing"', "Here's a quote"]) + + return response + +The code and comments should be self-explanatory, but a few things deserve a +mention: + + * The response gets a special MIME type, ``text/csv``. This tells + browsers that the document is a CSV file, rather than an HTML file. If + you leave this off, browsers will probably interpret the output as HTML, + which will result in ugly, scary gobbledygook in the browser window. + + * The response gets an additional ``Content-Disposition`` header, which + contains the name of the CSV file. This filename is arbitrary; call it + whatever you want. It'll be used by browsers in the "Save as..." + dialogue, etc. + + * Hooking into the CSV-generation API is easy: Just pass ``response`` as the + first argument to ``csv.writer``. The ``csv.writer`` function expects a + file-like object, and :class:`~django.http.HttpResponse` objects fit the + bill. + + * For each row in your CSV file, call ``writer.writerow``, passing it an + iterable object such as a list or tuple. + + * The CSV module takes care of quoting for you, so you don't have to worry + about escaping strings with quotes or commas in them. Just pass + ``writerow()`` your raw strings, and it'll do the right thing. + +Handling Unicode +~~~~~~~~~~~~~~~~ + +Python's ``csv`` module does not support Unicode input. Since Django uses +Unicode internally this means strings read from sources such as +:class:`~django.http.HttpRequest` are potentially problematic. There are a few +options for handling this: + + * Manually encode all Unicode objects to a compatible encoding. + + * Use the ``UnicodeWriter`` class provided in the `csv module's examples + section`_. + + * Use the `python-unicodecsv module`_, which aims to be a drop-in + replacement for ``csv`` that gracefully handles Unicode. + +For more information, see the Python `CSV File Reading and Writing`_ +documentation. + +.. _`csv module's examples section`: http://docs.python.org/library/csv.html#examples +.. _`python-unicodecsv module`: https://github.com/jdunck/python-unicodecsv +.. _`CSV File Reading and Writing`: http://docs.python.org/library/csv.html + +Using the template system +========================= + +Alternatively, you can use the :doc:`Django template system </topics/templates>` +to generate CSV. This is lower-level than using the convenient Python ``csv`` +module, but the solution is presented here for completeness. + +The idea here is to pass a list of items to your template, and have the +template output the commas in a :ttag:`for` loop. + +Here's an example, which generates the same CSV file as above:: + + from django.http import HttpResponse + from django.template import loader, Context + + def some_view(request): + # Create the HttpResponse object with the appropriate CSV header. + response = HttpResponse(mimetype='text/csv') + response['Content-Disposition'] = 'attachment; filename=somefilename.csv' + + # The data is hard-coded here, but you could load it from a database or + # some other source. + csv_data = ( + ('First row', 'Foo', 'Bar', 'Baz'), + ('Second row', 'A', 'B', 'C', '"Testing"', "Here's a quote"), + ) + + t = loader.get_template('my_template_name.txt') + c = Context({ + 'data': csv_data, + }) + response.write(t.render(c)) + return response + +The only difference between this example and the previous example is that this +one uses template loading instead of the CSV module. The rest of the code -- +such as the ``mimetype='text/csv'`` -- is the same. + +Then, create the template ``my_template_name.txt``, with this template code: + +.. code-block:: html+django + + {% for row in data %}"{{ row.0|addslashes }}", "{{ row.1|addslashes }}", "{{ row.2|addslashes }}", "{{ row.3|addslashes }}", "{{ row.4|addslashes }}" + {% endfor %} + +This template is quite basic. It just iterates over the given data and displays +a line of CSV for each row. It uses the :tfilter:`addslashes` template filter to +ensure there aren't any problems with quotes. + +Other text-based formats +======================== + +Notice that there isn't very much specific to CSV here -- just the specific +output format. You can use either of these techniques to output any text-based +format you can dream of. You can also use a similar technique to generate +arbitrary binary data; see :doc:`/howto/outputting-pdf` for an example. diff --git a/parts/django/docs/howto/outputting-pdf.txt b/parts/django/docs/howto/outputting-pdf.txt new file mode 100644 index 0000000..67950d0 --- /dev/null +++ b/parts/django/docs/howto/outputting-pdf.txt @@ -0,0 +1,160 @@ +=========================== +Outputting PDFs with Django +=========================== + +This document explains how to output PDF files dynamically using Django views. +This is made possible by the excellent, open-source ReportLab_ Python PDF +library. + +The advantage of generating PDF files dynamically is that you can create +customized PDFs for different purposes -- say, for different users or different +pieces of content. + +For example, Django was used at kusports.com_ to generate customized, +printer-friendly NCAA tournament brackets, as PDF files, for people +participating in a March Madness contest. + +.. _ReportLab: http://www.reportlab.org/oss/rl-toolkit/ +.. _kusports.com: http://www.kusports.com/ + +Install ReportLab +================= + +Download and install the ReportLab library from http://www.reportlab.org/oss/rl-toolkit/download/. +The `user guide`_ (not coincidentally, a PDF file) explains how to install it. + +Test your installation by importing it in the Python interactive interpreter:: + + >>> import reportlab + +If that command doesn't raise any errors, the installation worked. + +.. _user guide: http://www.reportlab.com/docs/reportlab-userguide.pdf + +Write your view +=============== + +The key to generating PDFs dynamically with Django is that the ReportLab API +acts on file-like objects, and Django's :class:`~django.http.HttpResponse` +objects are file-like objects. + +Here's a "Hello World" example:: + + from reportlab.pdfgen import canvas + from django.http import HttpResponse + + def some_view(request): + # Create the HttpResponse object with the appropriate PDF headers. + response = HttpResponse(mimetype='application/pdf') + response['Content-Disposition'] = 'attachment; filename=somefilename.pdf' + + # Create the PDF object, using the response object as its "file." + p = canvas.Canvas(response) + + # Draw things on the PDF. Here's where the PDF generation happens. + # See the ReportLab documentation for the full list of functionality. + p.drawString(100, 100, "Hello world.") + + # Close the PDF object cleanly, and we're done. + p.showPage() + p.save() + return response + +The code and comments should be self-explanatory, but a few things deserve a +mention: + + * The response gets a special MIME type, ``application/pdf``. This tells + browsers that the document is a PDF file, rather than an HTML file. If + you leave this off, browsers will probably interpret the output as HTML, + which would result in ugly, scary gobbledygook in the browser window. + + * The response gets an additional ``Content-Disposition`` header, which + contains the name of the PDF file. This filename is arbitrary: Call it + whatever you want. It'll be used by browsers in the "Save as..." + dialogue, etc. + + * The ``Content-Disposition`` header starts with ``'attachment; '`` in this + example. This forces Web browsers to pop-up a dialog box + prompting/confirming how to handle the document even if a default is set + on the machine. If you leave off ``'attachment;'``, browsers will handle + the PDF using whatever program/plugin they've been configured to use for + PDFs. Here's what that code would look like:: + + response['Content-Disposition'] = 'filename=somefilename.pdf' + + * Hooking into the ReportLab API is easy: Just pass ``response`` as the + first argument to ``canvas.Canvas``. The ``Canvas`` class expects a + file-like object, and :class:`~django.http.HttpResponse` objects fit the + bill. + + * Note that all subsequent PDF-generation methods are called on the PDF + object (in this case, ``p``) -- not on ``response``. + + * Finally, it's important to call ``showPage()`` and ``save()`` on the PDF + file. + +Complex PDFs +============ + +If you're creating a complex PDF document with ReportLab, consider using the +cStringIO_ library as a temporary holding place for your PDF file. The cStringIO +library provides a file-like object interface that is particularly efficient. +Here's the above "Hello World" example rewritten to use ``cStringIO``:: + + # Fall back to StringIO in environments where cStringIO is not available + try: + from cStringIO import StringIO + except ImportError: + from StringIO import StringIO + from reportlab.pdfgen import canvas + from django.http import HttpResponse + + def some_view(request): + # Create the HttpResponse object with the appropriate PDF headers. + response = HttpResponse(mimetype='application/pdf') + response['Content-Disposition'] = 'attachment; filename=somefilename.pdf' + + buffer = StringIO() + + # Create the PDF object, using the StringIO object as its "file." + p = canvas.Canvas(buffer) + + # Draw things on the PDF. Here's where the PDF generation happens. + # See the ReportLab documentation for the full list of functionality. + p.drawString(100, 100, "Hello world.") + + # Close the PDF object cleanly. + p.showPage() + p.save() + + # Get the value of the StringIO buffer and write it to the response. + pdf = buffer.getvalue() + buffer.close() + response.write(pdf) + return response + +.. _cStringIO: http://docs.python.org/library/stringio.html#module-cStringIO + +Further resources +================= + + * PDFlib_ is another PDF-generation library that has Python bindings. To + use it with Django, just use the same concepts explained in this article. + * `Pisa XHTML2PDF`_ is yet another PDF-generation library. Pisa ships with + an example of how to integrate Pisa with Django. + * HTMLdoc_ is a command-line script that can convert HTML to PDF. It + doesn't have a Python interface, but you can escape out to the shell + using ``system`` or ``popen`` and retrieve the output in Python. + +.. _PDFlib: http://www.pdflib.org/ +.. _`Pisa XHTML2PDF`: http://www.xhtml2pdf.com/ +.. _HTMLdoc: http://www.htmldoc.org/ + +Other formats +============= + +Notice that there isn't a lot in these examples that's PDF-specific -- just the +bits using ``reportlab``. You can use a similar technique to generate any +arbitrary format that you can find a Python library for. Also see +:doc:`/howto/outputting-csv` for another example and some techniques you can use +when generated text-based formats. diff --git a/parts/django/docs/howto/static-files.txt b/parts/django/docs/howto/static-files.txt new file mode 100644 index 0000000..c3692d5 --- /dev/null +++ b/parts/django/docs/howto/static-files.txt @@ -0,0 +1,162 @@ +========================= +How to serve static files +========================= + +.. module:: django.views.static + :synopsis: Serving of static files during development. + +Django itself doesn't serve static (media) files, such as images, style sheets, +or video. It leaves that job to whichever Web server you choose. + +The reasoning here is that standard Web servers, such as Apache_, lighttpd_ and +Cherokee_, are much more fine-tuned at serving static files than a Web +application framework. + +With that said, Django does support static files **during development**. You can +use the :func:`django.views.static.serve` view to serve media files. + +.. _Apache: http://httpd.apache.org/ +.. _lighttpd: http://www.lighttpd.net/ +.. _Cherokee: http://www.cherokee-project.com/ + +.. seealso:: + + If you just need to serve the admin media from a nonstandard location, see + the :djadminopt:`--adminmedia` parameter to :djadmin:`runserver`. + +The big, fat disclaimer +======================= + +Using this method is **inefficient** and **insecure**. Do not use this in a +production setting. Use this only for development. + +For information on serving static files in an Apache production environment, +see the :ref:`Django mod_python documentation <serving-media-files>`. + +How to do it +============ + +Here's the formal definition of the :func:`~django.views.static.serve` view: + +.. function:: def serve(request, path, document_root, show_indexes=False) + +To use it, just put this in your :doc:`URLconf </topics/http/urls>`:: + + (r'^site_media/(?P<path>.*)$', 'django.views.static.serve', + {'document_root': '/path/to/media'}), + +...where ``site_media`` is the URL where your media will be rooted, and +``/path/to/media`` is the filesystem root for your media. This will call the +:func:`~django.views.static.serve` view, passing in the path from the URLconf +and the (required) ``document_root`` parameter. + +Given the above URLconf: + + * The file ``/path/to/media/foo.jpg`` will be made available at the URL + ``/site_media/foo.jpg``. + + * The file ``/path/to/media/css/mystyles.css`` will be made available + at the URL ``/site_media/css/mystyles.css``. + + * The file ``/path/bar.jpg`` will not be accessible, because it doesn't + fall under the document root. + +Of course, it's not compulsory to use a fixed string for the +``'document_root'`` value. You might wish to make that an entry in your +settings file and use the setting value there. That will allow you and +other developers working on the code to easily change the value as +required. For example, if we have a line in ``settings.py`` that says:: + + STATIC_DOC_ROOT = '/path/to/media' + +...we could write the above :doc:`URLconf </topics/http/urls>` entry as:: + + from django.conf import settings + ... + (r'^site_media/(?P<path>.*)$', 'django.views.static.serve', + {'document_root': settings.STATIC_DOC_ROOT}), + +Be careful not to use the same path as your :setting:`ADMIN_MEDIA_PREFIX` (which defaults +to ``/media/``) as this will overwrite your URLconf entry. + +Directory listings +================== + +Optionally, you can pass the ``show_indexes`` parameter to the +:func:`~django.views.static.serve` view. This is ``False`` by default. If it's +``True``, Django will display file listings for directories. + +For example:: + + (r'^site_media/(?P<path>.*)$', 'django.views.static.serve', + {'document_root': '/path/to/media', 'show_indexes': True}), + +You can customize the index view by creating a template called +``static/directory_index.html``. That template gets two objects in its context: + + * ``directory`` -- the directory name (a string) + * ``file_list`` -- a list of file names (as strings) in the directory + +Here's the default ``static/directory_index.html`` template: + +.. code-block:: html+django + + <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> + <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> + <head> + <meta http-equiv="Content-type" content="text/html; charset=utf-8" /> + <meta http-equiv="Content-Language" content="en-us" /> + <meta name="robots" content="NONE,NOARCHIVE" /> + <title>Index of {{ directory }}</title> + </head> + <body> + <h1>Index of {{ directory }}</h1> + <ul> + {% for f in file_list %} + <li><a href="{{ f }}">{{ f }}</a></li> + {% endfor %} + </ul> + </body> + </html> + +.. versionchanged:: 1.0.3 + Prior to Django 1.0.3, there was a bug in the view that provided directory + listings. The template that was loaded had to be called + ``static/directory_listing`` (with no ``.html`` extension). For backwards + compatibility with earlier versions, Django will still load templates with + the older (no extension) name, but it will prefer the + ``directory_index.html`` version. + +Limiting use to DEBUG=True +========================== + +Because URLconfs are just plain Python modules, you can use Python logic to +make the static-media view available only in development mode. This is a handy +trick to make sure the static-serving view doesn't slip into a production +setting by mistake. + +Do this by wrapping an ``if DEBUG`` statement around the +:func:`django.views.static.serve` inclusion. Here's a full example URLconf:: + + from django.conf.urls.defaults import * + from django.conf import settings + + urlpatterns = patterns('', + (r'^articles/2003/$', 'news.views.special_case_2003'), + (r'^articles/(?P<year>\d{4})/$', 'news.views.year_archive'), + (r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/$', 'news.views.month_archive'), + (r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d+)/$', 'news.views.article_detail'), + ) + + if settings.DEBUG: + urlpatterns += patterns('', + (r'^site_media/(?P<path>.*)$', 'django.views.static.serve', {'document_root': '/path/to/media'}), + ) + +This code is straightforward. It imports the settings and checks the value of +the :setting:`DEBUG` setting. If it evaluates to ``True``, then ``site_media`` +will be associated with the ``django.views.static.serve`` view. If not, then the +view won't be made available. + +Of course, the catch here is that you'll have to remember to set ``DEBUG=False`` +in your production settings file. But you should be doing that anyway. |