diff options
| author | Justin Bronn <jbronn@gmail.com> | 2007-08-26 01:10:53 +0000 |
|---|---|---|
| committer | Justin Bronn <jbronn@gmail.com> | 2007-08-26 01:10:53 +0000 |
| commit | 2052b508eb92c62fc0678efd4936c5ec1e0e735b (patch) | |
| tree | e510109b74b28c8ccef5f6955727cb9dce3da655 /docs | |
| parent | a7297a255f4bb86f608ea251e00253d18c31d9d4 (diff) | |
gis: Made necessary modifications for unicode, manage refactor, backend refactor and merged 5584-6000 via svnmerge from [repos:django/trunk trunk].
git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@6018 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'docs')
44 files changed, 2605 insertions, 711 deletions
diff --git a/docs/add_ons.txt b/docs/add_ons.txt index ffc4f7420f..a1d78b8685 100644 --- a/docs/add_ons.txt +++ b/docs/add_ons.txt @@ -138,6 +138,29 @@ Examples: You can pass in either an integer or a string representation of an integer. +naturalday +---------- + +**New in Django development version** + +For dates that are the current day or within one day, return "today", +"tomorrow" or "yesterday", as appropriate. Otherwise, format the date using +the passed in format string. + +**Argument:** Date formatting string as described in default tag now_. + +.. _now: ../templates/#now + +Examples (when 'today' is 17 Feb 2007): + + * ``16 Feb 2007`` becomes ``yesterday``. + * ``17 Feb 2007`` becomes ``today``. + * ``18 Feb 2007`` becomes ``tomorrow``. + * Any other day is formatted according to given argument or the + `DATE_FORMAT`_ setting if no argument is given. + +.. _DATE_FORMAT: ../settings/#date_format + flatpages ========= @@ -214,7 +237,7 @@ A framework for generating syndication feeds, in RSS and Atom, quite easily. See the `syndication documentation`_. -.. _syndication documentation: ../syndication/ +.. _syndication documentation: ../syndication_feeds/ Other add-ons ============= diff --git a/docs/api_stability.txt b/docs/api_stability.txt index cfaffeac6b..5ccf104327 100644 --- a/docs/api_stability.txt +++ b/docs/api_stability.txt @@ -82,9 +82,6 @@ that 90% of Django can be considered forwards-compatible at this point. That said, these APIs should *not* be considered stable, and are likely to change: - - `Forms and validation`_ will most likely be completely rewritten to - deemphasize Manipulators in favor of validation-aware models. - - `Serialization`_ is under heavy development; changes are likely. - The `authentication`_ framework is changing to be far more flexible, and @@ -114,7 +111,7 @@ change: .. _sending email: ../email/ .. _sessions: ../sessions/ .. _settings: ../settings/ -.. _syndication: ../syndication/ +.. _syndication: ../syndication_feeds/ .. _template language: ../templates/ .. _transactions: ../transactions/ .. _url dispatch: ../url_dispatch/ diff --git a/docs/authentication.txt b/docs/authentication.txt index efe4d47513..7860b59d7d 100644 --- a/docs/authentication.txt +++ b/docs/authentication.txt @@ -99,7 +99,7 @@ custom methods: should prefer using ``is_authenticated()`` to this method. * ``is_authenticated()`` -- Always returns ``True``. This is a way to - tell if the user has been authenticated. This does not imply any + tell if the user has been authenticated. This does not imply any permissions, and doesn't check if the user is active - it only indicates that the user has provided a valid username and password. @@ -114,6 +114,18 @@ custom methods: string is the correct password for the user. (This takes care of the password hashing in making the comparison.) + * ``set_unusable_password()`` -- **New in Django development version.** + Marks the user as having no password set. This isn't the same as having + a blank string for a password. ``check_password()`` for this user will + never return ``True``. Doesn't save the ``User`` object. + + You may need this if authentication for your application takes place + against an existing external source such as an LDAP directory. + + * ``has_usable_password()`` -- **New in Django development version.** + Returns ``False`` if ``set_unusable_password()`` has been called for this + user. + * ``get_group_permissions()`` -- Returns a list of permission strings that the user has, through his/her groups. @@ -126,7 +138,7 @@ custom methods: * ``has_perms(perm_list)`` -- Returns ``True`` if the user has each of the specified permissions, where each perm is in the format - ``"package.codename"``. If the user is inactive, this method will + ``"package.codename"``. If the user is inactive, this method will always return ``False``. * ``has_module_perms(package_name)`` -- Returns ``True`` if the user has @@ -152,9 +164,11 @@ Manager functions The ``User`` model has a custom manager that has the following helper functions: - * ``create_user(username, email, password)`` -- Creates, saves and returns - a ``User``. The ``username``, ``email`` and ``password`` are set as - given, and the ``User`` gets ``is_active=True``. + * ``create_user(username, email, password=None)`` -- Creates, saves and + returns a ``User``. The ``username``, ``email`` and ``password`` are set + as given, and the ``User`` gets ``is_active=True``. + + If no password is provided, ``set_unusable_password()`` will be called. See _`Creating users` for example usage. @@ -220,7 +234,7 @@ the setting and checking of these values behind the scenes. Previous Django versions, such as 0.90, used simple MD5 hashes without password salts. For backwards compatibility, those are still supported; they'll be -converted automatically to the new style the first time ``check_password()`` +converted automatically to the new style the first time ``User.check_password()`` works correctly for a given user. Anonymous users @@ -422,7 +436,10 @@ template context variables: * ``next``: The URL to redirect to after successful login. This may contain a query string, too. * ``site_name``: The name of the current ``Site``, according to the - ``SITE_ID`` setting. See the `site framework docs`_. + ``SITE_ID`` setting. If you're using the Django development version and + you don't have the site framework installed, this will be set to the + value of ``request.META['SERVER_NAME']``. For more on sites, see the + `site framework docs`_. If you'd prefer not to call the template ``registration/login.html``, you can pass the ``template_name`` parameter via the extra arguments to the view in @@ -661,8 +678,6 @@ Example in Python 2.4 syntax:: The permission_required decorator ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -**New in Django development version** - It's a relatively common task to check whether a user has a particular permission. For that reason, Django provides a shortcut for that case: the ``permission_required()`` decorator. Using this decorator, the earlier example diff --git a/docs/cache.txt b/docs/cache.txt index e245e100e7..d13352b025 100644 --- a/docs/cache.txt +++ b/docs/cache.txt @@ -543,7 +543,7 @@ Order of MIDDLEWARE_CLASSES If you use ``CacheMiddleware``, it's important to put it in the right place within the ``MIDDLEWARE_CLASSES`` setting, because the cache middleware needs to know which headers by which to vary the cache storage. Middleware always -adds something the ``Vary`` response header when it can. +adds something to the ``Vary`` response header when it can. Put the ``CacheMiddleware`` after any middlewares that might add something to the ``Vary`` header. The following middlewares do so: diff --git a/docs/contributing.txt b/docs/contributing.txt index b3c7efa2f7..b0df62fe99 100644 --- a/docs/contributing.txt +++ b/docs/contributing.txt @@ -67,6 +67,13 @@ particular: likely to get lost. If a particular ticket is controversial, please move discussion to `django-developers`_. + * **Don't** post to django-developers just to announce that you have filed + a bug report. All the tickets are mailed to another list + (`django-updates`_), which is tracked by developers and triagers, so we + see them as they are filed. + +.. _django-updates: http://groups.google.com/group/django-updates + Reporting security issues ========================= @@ -279,6 +286,22 @@ Please follow these coding standards when writing code for inclusion in Django: * Mark all strings for internationalization; see the `i18n documentation`_ for details. + * In docstrings, use "action words" such as:: + + def foo(): + """ + Calculates something and returns the result. + """ + pass + + Here's an example of what not to do:: + + def foo(): + """ + Calculate something and return the result. + """ + pass + * Please don't put your name in the code you contribute. Our policy is to keep contributors' names in the ``AUTHORS`` file distributed with Django -- not scattered throughout the codebase itself. Feel free to include a @@ -324,14 +347,14 @@ Model style Do this:: class Person(models.Model): - first_name = models.CharField(maxlength=20) - last_name = models.CharField(maxlength=40) + first_name = models.CharField(max_length=20) + last_name = models.CharField(max_length=40) Don't do this:: class Person(models.Model): - FirstName = models.CharField(maxlength=20) - Last_Name = models.CharField(maxlength=40) + FirstName = models.CharField(max_length=20) + Last_Name = models.CharField(max_length=40) * The ``class Meta`` should appear *after* the fields are defined, with a single blank line separating the fields and the class definition. @@ -339,8 +362,8 @@ Model style Do this:: class Person(models.Model): - first_name = models.CharField(maxlength=20) - last_name = models.CharField(maxlength=40) + first_name = models.CharField(max_length=20) + last_name = models.CharField(max_length=40) class Meta: verbose_name_plural = 'people' @@ -348,8 +371,8 @@ Model style Don't do this:: class Person(models.Model): - first_name = models.CharField(maxlength=20) - last_name = models.CharField(maxlength=40) + first_name = models.CharField(max_length=20) + last_name = models.CharField(max_length=40) class Meta: verbose_name_plural = 'people' @@ -359,8 +382,8 @@ Model style class Meta: verbose_name_plural = 'people' - first_name = models.CharField(maxlength=20) - last_name = models.CharField(maxlength=40) + first_name = models.CharField(max_length=20) + last_name = models.CharField(max_length=40) * The order of model inner classes and standard methods should be as follows (noting that these are not all required): @@ -368,6 +391,7 @@ Model style * All database fields * ``class Meta`` * ``class Admin`` + * ``def __unicode__()`` * ``def __str__()`` * ``def save()`` * ``def get_absolute_url()`` @@ -393,11 +417,11 @@ Guidelines for ReST files These guidelines regulate the format of our ReST documentation: - * In section titles, capitalize only initial words and proper nouns. + * In section titles, capitalize only initial words and proper nouns. - * Wrap the documentation at 80 characters wide, unless a code example - is significantly less readable when split over two lines, or for another - good reason. + * Wrap the documentation at 80 characters wide, unless a code example + is significantly less readable when split over two lines, or for another + good reason. Commonly used terms ------------------- @@ -405,41 +429,41 @@ Commonly used terms Here are some style guidelines on commonly used terms throughout the documentation: - * **Django** -- when referring to the framework, capitalize Django. It is - lowercase only in Python code and in the djangoproject.com logo. + * **Django** -- when referring to the framework, capitalize Django. It is + lowercase only in Python code and in the djangoproject.com logo. - * **e-mail** -- it has a hyphen. + * **e-mail** -- it has a hyphen. - * **MySQL** + * **MySQL** - * **PostgreSQL** + * **PostgreSQL** - * **Python** -- when referring to the language, capitalize Python. + * **Python** -- when referring to the language, capitalize Python. - * **realize**, **customize**, **initialize**, etc. -- use the American - "ize" suffix, not "ise." + * **realize**, **customize**, **initialize**, etc. -- use the American + "ize" suffix, not "ise." - * **SQLite** + * **SQLite** - * **subclass** -- it's a single word without a hyphen, both as a verb - ("subclass that model") and as a noun ("create a subclass"). + * **subclass** -- it's a single word without a hyphen, both as a verb + ("subclass that model") and as a noun ("create a subclass"). - * **Web**, **World Wide Web**, **the Web** -- note Web is always - capitalized when referring to the World Wide Web. + * **Web**, **World Wide Web**, **the Web** -- note Web is always + capitalized when referring to the World Wide Web. * **Web site** -- use two words, with Web capitalized. Django-specific terminology --------------------------- - * **model** -- it's not capitalized. + * **model** -- it's not capitalized. - * **template** -- it's not capitalized. + * **template** -- it's not capitalized. - * **URLconf** -- use three capitalized letters, with no space before - "conf." + * **URLconf** -- use three capitalized letters, with no space before + "conf." - * **view** -- it's not capitalized. + * **view** -- it's not capitalized. Committing code =============== @@ -528,11 +552,9 @@ To run the tests, ``cd`` to the ``tests/`` directory and type:: ./runtests.py --settings=path.to.django.settings Yes, the unit tests need a settings module, but only for database connection -info -- the ``DATABASE_NAME`` (required, but will be ignored), -``DATABASE_ENGINE``, ``DATABASE_USER`` and ``DATABASE_PASSWORD`` settings. You -will also need a ``ROOT_URLCONF`` setting (its value is ignored; it just needs -to be present) and a ``SITE_ID`` setting (any integer value will do) in order -for all the tests to pass. +info, with the ``DATABASE_ENGINE`` setting. You will also need a ``ROOT_URLCONF`` +setting (its value is ignored; it just needs to be present) and a ``SITE_ID`` +setting (any non-zero integer value will do) in order for all the tests to pass. The unit tests will not touch your existing databases; they create a new database, called ``django_test_db``, which is deleted when the tests are diff --git a/docs/csrf.txt b/docs/csrf.txt index c12dd1d116..7d79e39502 100644 --- a/docs/csrf.txt +++ b/docs/csrf.txt @@ -41,10 +41,10 @@ CsrfMiddleware does two things: This ensures that only forms that have originated from your web site can be used to POST data back. -It deliberately only targets HTTP POST requests (and the corresponding -POST forms). GET requests ought never to have side effects (if you are -using HTTP GET and POST correctly), and so a CSRF attack with a GET -request will always be harmless. +It deliberately only targets HTTP POST requests (and the corresponding POST +forms). GET requests ought never to have any potentially dangerous side +effects (see `9.1.1 Safe Methods, HTTP 1.1, RFC 2616`_), and so a +CSRF attack with a GET request ought to be harmless. POST requests that are not accompanied by a session cookie are not protected, but they do not need to be protected, since the 'attacking' web site @@ -54,6 +54,8 @@ The Content-Type is checked before modifying the response, and only pages that are served as 'text/html' or 'application/xml+xhtml' are modified. +.. _9.1.1 Safe Methods, HTTP 1.1, RFC 2616: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html + Limitations =========== diff --git a/docs/databases.txt b/docs/databases.txt index b73f39843c..ed0cb61bc3 100644 --- a/docs/databases.txt +++ b/docs/databases.txt @@ -124,7 +124,7 @@ Several other MySQLdb connection options may be useful, such as ``ssl``, ``use_unicode``, ``init_command``, and ``sql_mode``. Consult the `MySQLdb documentation`_ for more details. -.. _settings documentation: http://www.djangoproject.com/documentation/settings/#database-engine +.. _settings documentation: ../settings/#database-engine .. _MySQL option file: http://dev.mysql.com/doc/refman/5.0/en/option-files.html .. _MySQLdb documentation: http://mysql-python.sourceforge.net/ diff --git a/docs/databrowse.txt b/docs/databrowse.txt index 9c03e7e4ea..81e9e8e83b 100644 --- a/docs/databrowse.txt +++ b/docs/databrowse.txt @@ -35,6 +35,7 @@ How to use Databrowse 2. Register a number of models with the Databrowse site:: from django.contrib import databrowse + from myapp.models import SomeModel, SomeOtherModel databrowse.site.register(SomeModel) databrowse.site.register(SomeOtherModel) diff --git a/docs/db-api.txt b/docs/db-api.txt index a4b920fb33..766a6ae519 100644 --- a/docs/db-api.txt +++ b/docs/db-api.txt @@ -12,27 +12,27 @@ Throughout this reference, we'll refer to the following models, which comprise a weblog application:: class Blog(models.Model): - name = models.CharField(maxlength=100) + name = models.CharField(max_length=100) tagline = models.TextField() - def __str__(self): + def __unicode__(self): return self.name class Author(models.Model): - name = models.CharField(maxlength=50) - email = models.URLField() + name = models.CharField(max_length=50) + email = models.EmailField() - def __str__(self): + def __unicode__(self): return self.name class Entry(models.Model): blog = models.ForeignKey(Blog) - headline = models.CharField(maxlength=255) + headline = models.CharField(max_length=255) body_text = models.TextField() pub_date = models.DateTimeField() authors = models.ManyToManyField(Author) - def __str__(self): + def __unicode__(self): return self.headline Creating objects @@ -118,6 +118,79 @@ happens. Explicitly specifying auto-primary-key values is mostly useful for bulk-saving objects, when you're confident you won't have primary-key collision. +What happens when you save? +--------------------------- + +When you save an object, Django performs the following steps: + + 1. **Emit a ``pre_save`` signal.** This provides a notification that + an object is about to be saved. You can register a listener that + will be invoked whenever this signal is emitted. (These signals are + not yet documented.) + + 2. **Pre-process the data.** Each field on the object is asked to + perform any automated data modification that the field may need + to perform. + + Most fields do *no* pre-processing -- the field data is kept as-is. + Pre-processing is only used on fields that have special behavior. + For example, if your model has a ``DateField`` with ``auto_now=True``, + the pre-save phase will alter the data in the object to ensure that + the date field contains the current date stamp. (Our documentation + doesn't yet include a list of all the fields with this "special + behavior.") + + 3. **Prepare the data for the database.** Each field is asked to provide + its current value in a data type that can be written to the database. + + Most fields require *no* data preparation. Simple data types, such as + integers and strings, are 'ready to write' as a Python object. However, + more complex data types often require some modification. + + For example, ``DateFields`` use a Python ``datetime`` object to store + data. Databases don't store ``datetime`` objects, so the field value + must be converted into an ISO-compliant date string for insertion + into the database. + + 4. **Insert the data into the database.** The pre-processed, prepared + data is then composed into an SQL statement for insertion into the + database. + + 5. **Emit a ``post_save`` signal.** As with the ``pre_save`` signal, this + is used to provide notification that an object has been successfully + saved. (These signals are not yet documented.) + +Raw Saves +~~~~~~~~~ + +**New in Django development version** + +The pre-processing step (#2 in the previous section) is useful, but it modifies +the data stored in a field. This can cause problems if you're relying upon the +data you provide being used as-is. + +For example, if you're setting up conditions for a test, you'll want the test +conditions to be repeatable. If pre-processing is performed, the data used +to specify test conditions may be modified, changing the conditions for the +test each time the test is run. + +In cases such as this, you need to prevent pre-processing from being performed +when you save an object. To do this, you can invoke a **raw save** by passing +``raw=True`` as an argument to the ``save()`` method:: + + b4.save(raw=True) # Save object, but do no pre-processing + +A raw save skips the usual data pre-processing that is performed during the +save. All other steps in the save (pre-save signal, data preparation, data +insertion, and post-save signal) are performed as normal. + +.. admonition:: When to use a raw save + + Generally speaking, you shouldn't need to use a raw save. Disabling field + pre-processing is an extraordinary measure that should only be required + in extraordinary circumstances, such as setting up reliable test + conditions. + Saving changes to objects ========================= @@ -140,7 +213,7 @@ object of the right type to the field in question:: joe = Author.objects.create(name="Joe") entry.author = joe entry.save() - + Django will complain if you try to assign an object of the wrong type. How Django knows to UPDATE vs. INSERT @@ -336,7 +409,7 @@ For example, this returns the first 5 objects (``LIMIT 5``):: Entry.objects.all()[:5] -This returns the fifth through tenth objects (``OFFSET 5 LIMIT 5``):: +This returns the sixth through tenth objects (``OFFSET 5 LIMIT 5``):: Entry.objects.all()[5:10] @@ -1430,7 +1503,7 @@ precede the definition of any keyword arguments. For example:: See the `OR lookups examples page`_ for more examples. -.. _OR lookups examples page: http://www.djangoproject.com/documentation/models/or_lookups/ +.. _OR lookups examples page: ../models/or_lookups/ Related objects =============== @@ -1733,8 +1806,8 @@ following model:: ('F', 'Female'), ) class Person(models.Model): - name = models.CharField(maxlength=20) - gender = models.CharField(maxlength=1, choices=GENDER_CHOICES) + name = models.CharField(max_length=20) + gender = models.CharField(max_length=1, choices=GENDER_CHOICES) ...each ``Person`` instance will have a ``get_gender_display()`` method. Example:: @@ -1761,7 +1834,7 @@ Note that in the case of identical date values, these methods will use the ID as a fallback check. This guarantees that no records are skipped or duplicated. For a full example, see the `lookup API sample model`_. -.. _lookup API sample model: http://www.djangoproject.com/documentation/models/lookup/ +.. _lookup API sample model: ../models/lookup/ get_FOO_filename() ------------------ @@ -1818,8 +1891,8 @@ get_object_or_404() One common idiom to use ``get()`` and raise ``Http404`` if the object doesn't exist. This idiom is captured by ``get_object_or_404()``. This function takes a Django model as its first argument and an -arbitrary number of keyword arguments, which it passes to the manager's -``get()`` function. It raises ``Http404`` if the object doesn't +arbitrary number of keyword arguments, which it passes to the default +manager's ``get()`` function. It raises ``Http404`` if the object doesn't exist. For example:: # Get the Entry with a primary key of 3 @@ -1828,7 +1901,7 @@ exist. For example:: When you provide a model to this shortcut function, the default manager is used to execute the underlying ``get()`` query. If you don't want to use the default manager, or if you want to search a list of related objects, -you can provide ``get_object_or_404()`` with a manager object instead. +you can provide ``get_object_or_404()`` with a ``Manager`` object instead. For example:: # Get the author of blog instance e with a name of 'Fred' @@ -1838,6 +1911,14 @@ For example:: # entry with a primary key of 3 e = get_object_or_404(Entry.recent_entries, pk=3) +**New in Django development version:** The first argument to +``get_object_or_404()`` can be a ``QuerySet`` object. This is useful in cases +where you've defined a custom manager method. For example:: + + # Use a QuerySet returned from a 'published' method of a custom manager + # in the search for an entry with primary key of 5 + e = get_object_or_404(Entry.objects.published(), pk=5) + get_list_or_404() ----------------- diff --git a/docs/distributions.txt b/docs/distributions.txt index 4ec265f93c..f9b9cbe9f8 100644 --- a/docs/distributions.txt +++ b/docs/distributions.txt @@ -21,16 +21,17 @@ Linux distributions Debian ------ -A `packaged version of Django`_ is available for `Debian GNU/Linux`_, and can be -installed from either the "testing" or the "unstable" repositories by typing -``apt-get install python-django``. +A `packaged version of Django`_ is available for `Debian GNU/Linux`_. Version +0.95.1 is available in the "stable" repository; Version 0.96 is available in +the "testing" and "unstable" repositories. Regardless of your chosen repository, +you can install Django by typing ``apt-get install python-django``. When you install this package, ``apt`` will recommend installing a database adapter; you should select and install the adapter for whichever database you plan to use with Django. .. _Debian GNU/Linux: http://www.debian.org/ -.. _packaged version of Django: http://packages.debian.org/testing/python/python-django +.. _packaged version of Django: http://packages.debian.org/stable/python/python-django Ubuntu ------ @@ -64,11 +65,24 @@ The `current Gentoo build`_ can be installed by typing ``emerge django``. .. _Gentoo Linux: http://www.gentoo.org/ .. _current Gentoo build: http://packages.gentoo.org/packages/?category=dev-python;name=django +Mac OS X +======== + +MacPorts +-------- + +Django 0.96 can be installed via the `MacPorts`_ system. If you're using Python 2.4, +type ``sudo port install py-django-devel``. For Python 2.5, type ``sudo port +install py25-django-devel``. MacPorts can also be used to install a database, +and the Python interface to your chosen database. + +.. _MacPorts: http://www.macports.org/ + For distributors ================ If you'd like to package Django for distribution, we'd be happy to help out! -Please join the `django-developers mailing list`_ and introduce yourself. +Please join the `django-developers mailing list`_ and introduce yourself. We also encourage all distributors to subscribe to the `django-announce mailing list`_, which is a (very) low-traffic list for announcing new releases of Django diff --git a/docs/django-admin.txt b/docs/django-admin.txt index 75c2738543..aea990c5dc 100644 --- a/docs/django-admin.txt +++ b/docs/django-admin.txt @@ -235,6 +235,7 @@ The ``dumpdata`` command can be used to generate input for ``loaddata``. reset [appname appname ...] --------------------------- + Executes the equivalent of ``sqlreset`` for the given appnames. runfcgi [options] @@ -400,6 +401,19 @@ install them in the database. This includes any apps shipped with Django that might be in ``INSTALLED_APPS`` by default. When you start a new project, run this command to install the default apps. +.. admonition:: Syncdb will not alter existing tables + + ``syncdb`` will only create tables for models which have not yet been + installed. It will *never* issue ``ALTER TABLE`` statements to match + changes made to a model class after installation. Changes to model classes + and database schemas often involve some form of ambiguity and, in those + cases, Django would have to guess at the correct changes to make. There is + a risk that critical data would be lost in the process. + + If you have made changes to a model and wish to alter the database tables + to match, use the ``sql`` command to display the new SQL structure and + compare that to your existing table schema to work out the changes. + If you're installing the ``django.contrib.auth`` application, ``syncdb`` will give you the option of creating a superuser immediately. @@ -413,7 +427,46 @@ test Discover and run tests for all installed models. See `Testing Django applications`_ for more information. -.. _testing django applications: ../testing/ +.. _testing Django applications: ../testing/ + +testserver [fixture fixture ...] +-------------------------------- + +**New in Django development version** + +Runs a Django development server (as in ``runserver``) using data from the +given fixture(s). + +For example, this command:: + + django-admin.py testserver mydata.json + +...would perform the following steps: + + 1. Create a test database, as described in `testing Django applications`_. + 2. Populate the test database with fixture data from the given fixtures. + (For more on fixtures, see the documentation for ``loaddata`` above.) + 3. Runs the Django development server (as in ``runserver``), pointed at + this newly created test database instead of your production database. + +This is useful in a number of ways: + + * When you're writing `unit tests`_ of how your views act with certain + fixture data, you can use ``testserver`` to interact with the views in + a Web browser, manually. + + * Let's say you're developing your Django application and have a "pristine" + copy of a database that you'd like to interact with. You can dump your + database to a fixture (using the ``dumpdata`` command, explained above), + then use ``testserver`` to run your Web application with that data. With + this arrangement, you have the flexibility of messing up your data + in any way, knowing that whatever data changes you're making are only + being made to a test database. + +Note that this server can only run on the default port on localhost; it does +not yet accept a ``host`` or ``port`` parameter. + +.. _unit tests: ../testing/ validate -------- diff --git a/docs/email.txt b/docs/email.txt index 50dafaf8df..97bdec0037 100644 --- a/docs/email.txt +++ b/docs/email.txt @@ -314,7 +314,7 @@ To send a text and HTML combination, you could write:: subject, from_email, to = 'hello', 'from@example.com', 'to@example.com' text_content = 'This is an important message.' - html_content = '<p>This is an <strong>important</strong> message.' + html_content = '<p>This is an <strong>important</strong> message.</p>' msg = EmailMultiAlternatives(subject, text_content, from_email, to) msg.attach_alternative(html_content, "text/html") msg.send() diff --git a/docs/faq.txt b/docs/faq.txt index 67ed8a49a5..844ea77809 100644 --- a/docs/faq.txt +++ b/docs/faq.txt @@ -42,7 +42,10 @@ Listen to his music. You'll like it. Django is pronounced **JANG**-oh. Rhymes with FANG-oh. The "D" is silent. +We've also recorded an `audio clip of the pronunciation`_. + .. _Django Reinhardt: http://en.wikipedia.org/wiki/Django_Reinhardt +.. _audio clip of the pronunciation: http://red-bean.com/~adrian/django_pronunciation.mp3 Is Django stable? ----------------- diff --git a/docs/fastcgi.txt b/docs/fastcgi.txt index 81888bba76..dff1689905 100644 --- a/docs/fastcgi.txt +++ b/docs/fastcgi.txt @@ -93,7 +93,7 @@ protocol by using the ``protocol=<protocol_name>`` 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 + ./manage.py runfcgi protocol=scgi .. _flup: http://www.saddi.com/software/flup/ .. _fastcgi: http://www.fastcgi.com/ diff --git a/docs/forms.txt b/docs/forms.txt index f6cb55a3f6..18d322a8eb 100644 --- a/docs/forms.txt +++ b/docs/forms.txt @@ -37,17 +37,17 @@ this document, we'll be working with the following model, a "place" object:: ) class Place(models.Model): - name = models.CharField(maxlength=100) - address = models.CharField(maxlength=100, blank=True) - city = models.CharField(maxlength=50, blank=True) + name = models.CharField(max_length=100) + address = models.CharField(max_length=100, blank=True) + city = models.CharField(max_length=50, blank=True) state = models.USStateField() - zip_code = models.CharField(maxlength=5, blank=True) + zip_code = models.CharField(max_length=5, blank=True) place_type = models.IntegerField(choices=PLACE_TYPES) class Admin: pass - def __str__(self): + def __unicode__(self): return self.name Defining the above class is enough to create an admin interface to a ``Place``, @@ -388,7 +388,7 @@ for a "contact" form on a website:: def __init__(self): self.fields = ( forms.EmailField(field_name="from", is_required=True), - forms.TextField(field_name="subject", length=30, maxlength=200, is_required=True), + forms.TextField(field_name="subject", length=30, max_length=200, is_required=True), forms.SelectField(field_name="urgency", choices=urgency_choices), forms.LargeTextField(field_name="contents", is_required=True), ) diff --git a/docs/generic_views.txt b/docs/generic_views.txt index 2b80348903..0601aead11 100644 --- a/docs/generic_views.txt +++ b/docs/generic_views.txt @@ -40,7 +40,7 @@ simple weblog app that drives the blog on djangoproject.com:: } urlpatterns = patterns('django.views.generic.date_based', - (r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\w{1,2})/(?P<slug>[-\w]+)/$', 'object_detail', dict(info_dict, slug_field='slug')), + (r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\w{1,2})/(?P<slug>[-\w]+)/$', 'object_detail', info_dict), (r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\w{1,2})/$', 'archive_day', info_dict), (r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$', 'archive_month', info_dict), (r'^(?P<year>\d{4})/$', 'archive_year', info_dict), @@ -603,7 +603,7 @@ future, the view will throw a 404 error by default, unless you set Otherwise, ``slug`` should be the slug of the given object, and ``slug_field`` should be the name of the slug field in the ``QuerySet``'s - model. + model. By default, ``slug_field`` is ``'slug'``. **Optional arguments:** @@ -804,7 +804,7 @@ A page representing an individual object. Otherwise, ``slug`` should be the slug of the given object, and ``slug_field`` should be the name of the slug field in the ``QuerySet``'s - model. + model. By default, ``slug_field`` is ``'slug'``. **Optional arguments:** @@ -948,7 +948,7 @@ object. This uses the automatic manipulators that come with Django models. Otherwise, ``slug`` should be the slug of the given object, and ``slug_field`` should be the name of the slug field in the ``QuerySet``'s - model. + model. By default, ``slug_field`` is ``'slug'``. **Optional arguments:** @@ -1033,7 +1033,7 @@ contain a form that POSTs to the same URL. Otherwise, ``slug`` should be the slug of the given object, and ``slug_field`` should be the name of the slug field in the ``QuerySet``'s - model. + model. By default, ``slug_field`` is ``'slug'``. * ``post_delete_redirect``: A URL to which the view will redirect after deleting the object. diff --git a/docs/i18n.txt b/docs/i18n.txt index 27abadacc9..38252edeb1 100644 --- a/docs/i18n.txt +++ b/docs/i18n.txt @@ -68,23 +68,41 @@ In Python code Standard translation ~~~~~~~~~~~~~~~~~~~~ -Specify a translation string by using the function ``_()``. (Yes, the name of -the function is the "underscore" character.) This function is available -globally in any Python module; you don't have to import it. +Specify a translation string by using the function ``ugettext()``. It's +convention to import this as a shorter alias, ``_``, to save typing. + +.. note:: + Python's standard library ``gettext`` module installs ``_()`` into the + global namespace, as an alias for ``gettext()``. In Django, we have chosen + not to follow this practice, for a couple of reasons: + + 1. For international character set (Unicode) support, ``ugettext()`` is + more useful than ``gettext()``. Sometimes, you should be using + ``ugettext_lazy()`` as the default translation method for a particular + file. Without ``_()`` in the global namespace, the developer has to + think about which is the most appropriate translation function. + + 2. The underscore character (``_``) is used to represent "the previous + result" in Python's interactive shell and doctest tests. Installing a + global ``_()`` function causes interference. Explicitly importing + ``ugettext()`` as ``_()`` avoids this problem. In this example, the text ``"Welcome to my site."`` is marked as a translation string:: + from django.utils.translation import ugettext as _ + def my_view(request): output = _("Welcome to my site.") return HttpResponse(output) -The function ``django.utils.translation.gettext()`` is identical to ``_()``. -This example is identical to the previous one:: +Obviously, you could code this without using the alias. This example is +identical to the previous one:: + + from django.utils.translation import ugettext - from django.utils.translation import gettext def my_view(request): - output = gettext("Welcome to my site.") + output = ugettext("Welcome to my site.") return HttpResponse(output) Translation works on computed values. This example is identical to the previous @@ -107,7 +125,7 @@ examples, is that Django's translation-string-detecting utility, ``make-messages.py``, won't be able to find these strings. More on ``make-messages`` later.) -The strings you pass to ``_()`` or ``gettext()`` can take placeholders, +The strings you pass to ``_()`` or ``ugettext()`` can take placeholders, specified with Python's standard named-string interpolation syntax. Example:: def my_view(request, n): @@ -120,14 +138,14 @@ while a Spanish translation may be ``"Me llamo Adrian."`` -- with the placeholder (the name) placed after the translated text instead of before it. For this reason, you should use named-string interpolation (e.g., ``%(name)s``) -instead of positional interpolation (e.g., ``%s`` or ``%d``). If you used -positional interpolation, translations wouldn't be able to reorder placeholder -text. +instead of positional interpolation (e.g., ``%s`` or ``%d``) whenever you +have more than a single parameter. If you used positional interpolation, +translations wouldn't be able to reorder placeholder text. Marking strings as no-op ~~~~~~~~~~~~~~~~~~~~~~~~ -Use the function ``django.utils.translation.gettext_noop()`` to mark a string +Use the function ``django.utils.translation.ugettext_noop()`` to mark a string as a translation string without translating it. The string is later translated from a variable. @@ -139,35 +157,35 @@ as when the string is presented to the user. Lazy translation ~~~~~~~~~~~~~~~~ -Use the function ``django.utils.translation.gettext_lazy()`` to translate +Use the function ``django.utils.translation.ugettext_lazy()`` to translate strings lazily -- when the value is accessed rather than when the -``gettext_lazy()`` function is called. +``ugettext_lazy()`` function is called. For example, to translate a model's ``help_text``, do the following:: - from django.utils.translation import gettext_lazy + from django.utils.translation import ugettext_lazy class MyThing(models.Model): - name = models.CharField(help_text=gettext_lazy('This is the help text')) + name = models.CharField(help_text=ugettext_lazy('This is the help text')) -In this example, ``gettext_lazy()`` stores a lazy reference to the string -- +In this example, ``ugettext_lazy()`` stores a lazy reference to the string -- not the actual translation. The translation itself will be done when the string is used in a string context, such as template rendering on the Django admin site. -If you don't like the verbose name ``gettext_lazy``, you can just alias it as +If you don't like the verbose name ``ugettext_lazy``, you can just alias it as ``_`` (underscore), like so:: - from django.utils.translation import gettext_lazy as _ + from django.utils.translation import ugettext_lazy as _ class MyThing(models.Model): name = models.CharField(help_text=_('This is the help text')) -Always use lazy translations in `Django models`_. And it's a good idea to add +Always use lazy translations in `Django models`_. It's a good idea to add translations for the field names and table names, too. This means writing explicit ``verbose_name`` and ``verbose_name_plural`` options in the ``Meta`` class, though:: - from django.utils.translation import gettext_lazy as _ + from django.utils.translation import ugettext_lazy as _ class MyThing(models.Model): name = models.CharField(_('name'), help_text=_('This is the help text')) @@ -180,24 +198,24 @@ class, though:: Pluralization ~~~~~~~~~~~~~ -Use the function ``django.utils.translation.ngettext()`` to specify pluralized +Use the function ``django.utils.translation.ungettext()`` to specify pluralized messages. Example:: - from django.utils.translation import ngettext + from django.utils.translation import ungettext def hello_world(request, count): - page = ngettext('there is %(count)d object', 'there are %(count)d objects', count) % { + page = ungettext('there is %(count)d object', 'there are %(count)d objects', count) % { 'count': count, } return HttpResponse(page) -``ngettext`` takes three arguments: the singular translation string, the plural +``ungettext`` takes three arguments: the singular translation string, the plural translation string and the number of objects (which is passed to the translation languages as the ``count`` variable). In template code ---------------- -Using translations in `Django templates`_ uses two template tags and a slightly +Translations in `Django templates`_ uses two template tags and a slightly different syntax than in Python code. To give your template access to these tags, put ``{% load i18n %}`` toward the top of your template. @@ -243,9 +261,9 @@ To pluralize, specify both the singular and plural forms with the {% endblocktrans %} Internally, all block and inline translations use the appropriate -``gettext`` / ``ngettext`` call. +``ugettext`` / ``ungettext`` call. -Each ``RequestContext`` has access to two translation-specific variables: +Each ``RequestContext`` has access to three translation-specific variables: * ``LANGUAGES`` is a list of tuples in which the first element is the language code and the second is the language name (in that language). @@ -276,6 +294,71 @@ string, so they don't need to be aware of translations. .. _Django templates: ../templates_python/ +Working with lazy translation objects +===================================== + +Using ``ugettext_lazy()`` and ``ungettext_lazy()`` to mark strings in models +and utility functions is a common operation. When you're working with these +objects elsewhere in your code, you should ensure that you don't accidentally +convert them to strings, because they should be converted as late as possible +(so that the correct locale is in effect). This necessitates the use of a +couple of helper functions. + +Joining strings: string_concat() +-------------------------------- + +Standard Python string joins (``''.join([...])``) will not work on lists +containing lazy translation objects. Instead, you can use +``django.utils.translation.string_concat()``, which creates a lazy object that +concatenates its contents *and* converts them to strings only when the result +is included in a string. For example:: + + from django.utils.translation import string_concat + ... + name = ugettext_lazy(u'John Lennon') + instrument = ugettext_lazy(u'guitar') + result = string_concat([name, ': ', instrument]) + +In this case, the lazy translations in ``result`` will only be converted to +strings when ``result`` itself is used in a string (usually at template +rendering time). + +The allow_lazy() decorator +-------------------------- + +Django offers many utility functions (particularly in ``django.utils``) that +take a string as their first argument and do something to that string. These +functions are used by template filters as well as directly in other code. + +If you write your own similar functions and deal with translations, you'll +face the problem of what to do when the first argument is a lazy translation +object. You don't want to convert it to a string immediately, because you might +be using this function outside of a view (and hence the current thread's locale +setting will not be correct). + +For cases like this, use the ``django.utils.functional.allow_lazy()`` +decorator. It modifies the function so that *if* it's called with a lazy +translation as the first argument, the function evaluation is delayed until it +needs to be converted to a string. + +For example:: + + from django.utils.functional import allow_lazy + + def fancy_utility_function(s, ...): + # Do some conversion on string 's' + ... + fancy_utility_function = allow_lazy(fancy_utility_function, unicode) + +The ``allow_lazy()`` decorator takes, in addition to the function to decorate, +a number of extra arguments (``*args``) specifying the type(s) that the +original function can return. Usually, it's enough to include ``unicode`` here +and ensure that your function returns only Unicode strings. + +Using this decorator means you can write your function and assume that the +input is a proper string, then add support for lazy translation objects at the +end. + How to create language files ============================ @@ -487,26 +570,26 @@ Notes: * If you define a custom ``LANGUAGES`` setting, as explained in the previous bullet, it's OK to mark the languages as translation strings - -- but use a "dummy" ``gettext()`` function, not the one in + -- but use a "dummy" ``ugettext()`` function, not the one in ``django.utils.translation``. You should *never* import ``django.utils.translation`` from within your settings file, because that module in itself depends on the settings, and that would cause a circular import. - The solution is to use a "dummy" ``gettext()`` function. Here's a sample + The solution is to use a "dummy" ``ugettext()`` function. Here's a sample settings file:: - gettext = lambda s: s + ugettext = lambda s: s LANGUAGES = ( - ('de', gettext('German')), - ('en', gettext('English')), + ('de', ugettext('German')), + ('en', ugettext('English')), ) With this arrangement, ``make-messages.py`` will still find and mark these strings for translation, but the translation won't happen at runtime -- so you'll have to remember to wrap the languages in the *real* - ``gettext()`` in any code that uses ``LANGUAGES`` at runtime. + ``ugettext()`` in any code that uses ``LANGUAGES`` at runtime. * The ``LocaleMiddleware`` can only select languages for which there is a Django-provided base translation. If you want to provide translations @@ -712,23 +795,23 @@ interface to access it:: document.write(gettext('this is to be translated')); -There even is a ``ngettext`` interface and a string interpolation function:: +There even is a ``ungettext`` interface and a string interpolation function:: d = { count: 10 }; - s = interpolate(ngettext('this is %(count)s object', 'this are %(count)s objects', d.count), d); + s = interpolate(ungettext('this is %(count)s object', 'this are %(count)s objects', d.count), d); The ``interpolate`` function supports both positional interpolation and named interpolation. So the above could have been written as:: - s = interpolate(ngettext('this is %s object', 'this are %s objects', 11), [11]); + s = interpolate(ungettext('this is %s object', 'this are %s objects', 11), [11]); The interpolation syntax is borrowed from Python. You shouldn't go over the top with string interpolation, though: this is still JavaScript, so the code will have to do repeated regular-expression substitutions. This isn't as fast as string interpolation in Python, so keep it to those cases where you really -need it (for example, in conjunction with ``ngettext`` to produce proper +need it (for example, in conjunction with ``ungettext`` to produce proper pluralizations). Creating JavaScript translation catalogs @@ -750,16 +833,13 @@ Specialities of Django translation If you know ``gettext``, you might note these specialities in the way Django does translation: - * The string domain is ``django`` or ``djangojs``. The string domain is used to - differentiate between different programs that store their data in a - common message-file library (usually ``/usr/share/locale/``). The ``django`` - domain is used for python and template translation strings and is loaded into - the global translation catalogs. The ``djangojs`` domain is only used for - JavaScript translation catalogs to make sure that those are as small as - possible. - * Django only uses ``gettext`` and ``gettext_noop``. That's because Django - always uses ``DEFAULT_CHARSET`` strings internally. There isn't much use - in using ``ugettext``, because you'll always need to produce utf-8 - anyway. + * The string domain is ``django`` or ``djangojs``. The string domain is + used to differentiate between different programs that store their data + in a common message-file library (usually ``/usr/share/locale/``). The + ``django`` domain is used for python and template translation strings + and is loaded into the global translation catalogs. The ``djangojs`` + domain is only used for JavaScript translation catalogs to make sure + that those are as small as possible. * Django doesn't use ``xgettext`` alone. It uses Python wrappers around ``xgettext`` and ``msgfmt``. That's mostly for convenience. + diff --git a/docs/install.txt b/docs/install.txt index e850e48955..082000149f 100644 --- a/docs/install.txt +++ b/docs/install.txt @@ -48,8 +48,8 @@ Get your database running If you plan to use Django's database API functionality, you'll need to make sure a database server is running. Django works with PostgreSQL_, -MySQL_, Oracle_ and SQLite_ (the latter doesn't require a separate server to -be running). +MySQL_, Oracle_ and SQLite_ (although SQLite doesn't require a separate server +to be running). Additionally, you'll need to make sure your Python database bindings are installed. @@ -109,25 +109,29 @@ Install the Django code ======================= Installation instructions are slightly different depending on whether you're -using the latest official version or the latest development version. +installing a distribution-specific package, downloading the the latest official +release, or fetching the latest development version. -It's easy either way. +It's easy, no matter which way you choose. -Installing the official version -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Installing a distribution-specific package +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - 1. Check the `distribution specific notes`_ to see if your - platform/distribution provides official Django packages/installers. - Distribution-provided packages will typically allow for automatic - installation of dependancies and easy upgrade paths. +Check the `distribution specific notes`_ to see if your +platform/distribution provides official Django packages/installers. +Distribution-provided packages will typically allow for automatic +installation of dependancies and easy upgrade paths. - 2. Download the latest release from our `download page`_. +Installing an official release +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - 3. Untar the downloaded file (e.g. ``tar xzvf Django-NNN.tar.gz``). + 1. Download the latest release from our `download page`_. - 4. Change into the downloaded directory (e.g. ``cd Django-NNN``). + 2. Untar the downloaded file (e.g. ``tar xzvf Django-NNN.tar.gz``). - 5. Run ``sudo python setup.py install``. + 3. Change into the downloaded directory (e.g. ``cd Django-NNN``). + + 4. Run ``sudo python setup.py install``. The command will install Django in your Python installation's ``site-packages`` directory. diff --git a/docs/man/compile-messages.1 b/docs/man/compile-messages.1 new file mode 100644 index 0000000000..d26a94aca7 --- /dev/null +++ b/docs/man/compile-messages.1 @@ -0,0 +1,40 @@ +.TH "compile-messages.py" "1" "August 2007" "Django Project" "" +.SH "NAME" +compile-messages.py \- Internationalization utility for the Django +web framework +.SH "SYNOPSIS" +.B compile-messages.py \fR[-l <locale>] + +.SH "DESCRIPTION" +A Django-customised wrapper around gettext's \fBmsgfmt\fR command. Generates +binary message catalogs (.mo files) from textual translation descriptions (.po +files). +.sp +The script should be invoked after running +.BI make-messages.py, +in the same directory from which +.BI make-messages.py +was invoked. + +.SH "OPTIONS" +.TP +.I \-l <locale> +Compile the message catalogs for a specific locale. If this option is omitted, +all message catalogs are (re-)compiled. + +.SH "SEE ALSO" +The man page for +.BI msgfmt +from the GNU gettext utilities, and the internationalization documentation +for Django: +.sp +.I http://www.djangoproject.com/documentation/i18n/ + +.SH "AUTHORS/CREDITS" +Originally developed at World Online in Lawrence, Kansas, USA. Refer to the +AUTHORS file in the Django distribution for contributors. + +.SH "LICENSE" +New BSD license. For the full license text refer to the LICENSE file in the +Django distribution. + diff --git a/docs/man/daily_cleanup.1 b/docs/man/daily_cleanup.1 new file mode 100644 index 0000000000..9186dd67d6 --- /dev/null +++ b/docs/man/daily_cleanup.1 @@ -0,0 +1,34 @@ +.TH "daily_cleanup.py" "1" "August 2007" "Django Project" "" +.SH "NAME" +daily_cleanup.py \- Database clean-up for the Django web framework +.SH "SYNOPSIS" +.B daily_cleanup.py + +.SH "DESCRIPTION" +Removes stale session data from a Django database. This means, any session data +which has an expiry date prior to the date the script is run. +.sp +The script can be run manually or can be scheduled to run at regular +intervals as a +.BI cron +job. + +.SH "ENVIRONMENT" +.TP +.I DJANGO_SETTINGS_MODULE +This environment variable defines the settings module to be read. +It should be in Python-import form, e.g. "myproject.settings". + +.SH "SEE ALSO" +The sessions documentation: +.sp +.I http://www.djangoproject.com/documentation/sessions/ + +.SH "AUTHORS/CREDITS" +Originally developed at World Online in Lawrence, Kansas, USA. Refer to the +AUTHORS file in the Django distribution for contributors. + +.SH "LICENSE" +New BSD license. For the full license text refer to the LICENSE file in the +Django distribution. + diff --git a/docs/man/gather_profile_stats.1 b/docs/man/gather_profile_stats.1 new file mode 100644 index 0000000000..5ff13d8e69 --- /dev/null +++ b/docs/man/gather_profile_stats.1 @@ -0,0 +1,26 @@ +.TH "gather_profile_stats.py" "1" "August 2007" "Django Project" "" +.SH "NAME" +gather_profile_stats.py \- Performance analysis tool for the Django web +framework +.SH "SYNOPSIS" +.B python gather_profile_stats.py +.I <path> + +.SH "DESCRIPTION" +This utility script aggregates profiling logs generated using Python's +hotshot profiler. The sole command-line argument is the full path to the +directory containing the profiling logfiles. + +.SH "SEE ALSO" +Discussion of profiling Django applications on the Django project's wiki: +.sp +.I http://www.djangoproject.com/wiki/ProfilingDjango + +.SH "AUTHORS/CREDITS" +Originally developed at World Online in Lawrence, Kansas, USA. Refer to the +AUTHORS file in the Django distribution for contributors. + +.SH "LICENSE" +New BSD license. For the full license text refer to the LICENSE file in the +Django distribution. + diff --git a/docs/man/make-messages.1 b/docs/man/make-messages.1 new file mode 100644 index 0000000000..b8c83dcff5 --- /dev/null +++ b/docs/man/make-messages.1 @@ -0,0 +1,62 @@ +.TH "make-messages.py" "1" "August 2007" "Django Project" "" +.SH "NAME" +make-messages.py \- Internationalization utility for the Django +web framework +.SH "SYNOPSIS" +.B make-messages.py\fR [\-a] [\-v] [\-l <locale>] [\-d <domain>] + +.SH "DESCRIPTION" +This script creates or updates one or more message files for a Django app, +a Django project or the Django framework itself. It should be run from one +of three places: the root directory of a Django app; the root directory +of a Django project; or the root django directory (the one in your PYTHONPATH, +not the root of a Subversion checkout). +.sp +The script will run over the source tree of an application, project or Django +itself (depending on where it is invoked), pulling out all strings marked for +translation and creating or updating a standard PO-format message file for the +specified language. Refer to Django's internationalization documentation for +details of where this file is created. +.sp +The \fI\-a\fR and \fI\-l\fR options are used to control whether message +catalogs are created for all locales, or just a single one. + +.SH "OPTIONS" +.TP +.I \-a +Run make-messages for all locales specified in the Django settings file. Cannot +be used in conjuntion with \fI\-l\fR. +.TP +.I \-d <domain> +Specifies the translation domain to use. Valid domains are \fIdjango\fR or +\fIdjangojs\fR, depending on whether you wish to generate translation strings +for the Python or JavaScript components of your app, your project or the +framework itself. The default domain is \fIdjango\fR. +.TP +.I \-l <locale> +Extract messages for a particular locale. +.TP +.I \-v +Run verbosely. + +.SH "ENVIRONMENT" +.TP +.I DJANGO_SETTINGS_MODULE +This environment variable defines the settings module to be read. +It should be in Python-import form, e.g. "myproject.settings". + +.SH "SEE ALSO" +The Django internationalization documentation: +.sp +.I http://www.djangoproject.com/documentation/i18n/ +.sp +The PO file format is documented in the GNU gettext documentation. + +.SH "AUTHORS/CREDITS" +Originally developed at World Online in Lawrence, Kansas, USA. Refer to the +AUTHORS file in the Django distribution for contributors. + +.SH "LICENSE" +New BSD license. For the full license text refer to the LICENSE file in the +Django distribution. + diff --git a/docs/middleware.txt b/docs/middleware.txt index 0d533443d3..63ba8c6999 100644 --- a/docs/middleware.txt +++ b/docs/middleware.txt @@ -91,6 +91,12 @@ django.middleware.gzip.GZipMiddleware Compresses content for browsers that understand gzip compression (all modern browsers). +It is suggested to place this first in the middleware list, so that the +compression of the response content is the last thing that happens. Will not +compress content bodies less than 200 bytes long, when the response code is +something other than 200, Javascript files (for IE compatibitility), or +responses that have the ``Content-Encoding`` header already specified. + django.middleware.http.ConditionalGetMiddleware ----------------------------------------------- @@ -116,6 +122,14 @@ not use this middleware. Anybody can spoof the value of ``HTTP_X_FORWARDED_FOR``, that means anybody can "fake" their IP address. Only use this when you can absolutely trust the value of ``HTTP_X_FORWARDED_FOR``. +django.middleware.locale.LocaleMiddleware +----------------------------------------- + +Enables language selection based on data from the request. It customizes content +for each user. See the `internationalization documentation`_. + +.. _`internationalization documentation`: ../i18n/ + django.contrib.sessions.middleware.SessionMiddleware ---------------------------------------------------- diff --git a/docs/model-api.txt b/docs/model-api.txt index 22ff7445b5..0f872c3097 100644 --- a/docs/model-api.txt +++ b/docs/model-api.txt @@ -22,7 +22,7 @@ A companion to this document is the `official repository of model examples`_. ``tests/modeltests`` directory.) .. _Database API reference: ../db-api/ -.. _official repository of model examples: http://www.djangoproject.com/documentation/models/ +.. _official repository of model examples: ../models/ Quick example ============= @@ -33,8 +33,8 @@ This example model defines a ``Person``, which has a ``first_name`` and from django.db import models class Person(models.Model): - first_name = models.CharField(maxlength=30) - last_name = models.CharField(maxlength=30) + first_name = models.CharField(max_length=30) + last_name = models.CharField(max_length=30) ``first_name`` and ``last_name`` are *fields* of the model. Each field is specified as a class attribute, and each attribute maps to a database column. @@ -69,13 +69,13 @@ attributes. Example:: class Musician(models.Model): - first_name = models.CharField(maxlength=50) - last_name = models.CharField(maxlength=50) - instrument = models.CharField(maxlength=100) + first_name = models.CharField(max_length=50) + last_name = models.CharField(max_length=50) + instrument = models.CharField(max_length=100) class Album(models.Model): artist = models.ForeignKey(Musician) - name = models.CharField(maxlength=100) + name = models.CharField(max_length=100) release_date = models.DateField() num_stars = models.IntegerField() @@ -142,14 +142,18 @@ For large amounts of text, use ``TextField``. The admin represents this as an ``<input type="text">`` (a single-line input). -``CharField`` has an extra required argument, ``maxlength``, the maximum length -(in characters) of the field. The maxlength is enforced at the database level +``CharField`` has an extra required argument, ``max_length``, the maximum length +(in characters) of the field. The max_length is enforced at the database level and in Django's validation. +Django veterans: Note that the argument is now called ``max_length`` to +provide consistency throughout Django. There is full legacy support for +the old ``maxlength`` argument, but ``max_length`` is prefered. + ``CommaSeparatedIntegerField`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -A field of integers separated by commas. As in ``CharField``, the ``maxlength`` +A field of integers separated by commas. As in ``CharField``, the ``max_length`` argument is required. ``DateField`` @@ -217,7 +221,7 @@ The admin represents this as an ``<input type="text">`` (a single-line input). ~~~~~~~~~~~~~~ A ``CharField`` that checks that the value is a valid e-mail address. -This doesn't accept ``maxlength``; its ``maxlength`` is automatically set to +This doesn't accept ``max_length``; its ``max_length`` is automatically set to 75. ``FileField`` @@ -400,7 +404,7 @@ Like a ``PositiveIntegerField``, but only allows values under a certain containing only letters, numbers, underscores or hyphens. They're generally used in URLs. -Like a CharField, you can specify ``maxlength``. If ``maxlength`` is +Like a CharField, you can specify ``max_length``. If ``max_length`` is not specified, Django will use a default length of 50. Implies ``db_index=True``. @@ -411,7 +415,8 @@ form:: models.SlugField(prepopulate_from=("pre_name", "name")) -``prepopulate_from`` doesn't accept DateTimeFields. +``prepopulate_from`` doesn't accept DateTimeFields, ForeignKeys nor +ManyToManyFields. The admin represents ``SlugField`` as an ``<input type="text">`` (a single-line input). @@ -447,9 +452,9 @@ and doesn't give a 404 response). The admin represents this as an ``<input type="text">`` (a single-line input). -``URLField`` takes an optional argument, ``maxlength``, the maximum length (in -characters) of the field. The maxlength is enforced at the database level and -in Django's validation. If you don't specify ``maxlength``, a default of 200 +``URLField`` takes an optional argument, ``max_length``, the maximum length (in +characters) of the field. The maximum length is enforced at the database level and +in Django's validation. If you don't specify ``max_length``, a default of 200 is used. ``USStateField`` @@ -536,7 +541,7 @@ The choices list can be defined either as part of your model class:: ('M', 'Male'), ('F', 'Female'), ) - gender = models.CharField(maxlength=1, choices=GENDER_CHOICES) + gender = models.CharField(max_length=1, choices=GENDER_CHOICES) or outside your model class altogether:: @@ -545,7 +550,7 @@ or outside your model class altogether:: ('F', 'Female'), ) class Foo(models.Model): - gender = models.CharField(maxlength=1, choices=GENDER_CHOICES) + gender = models.CharField(max_length=1, choices=GENDER_CHOICES) For each model field that has ``choices`` set, Django will add a method to retrieve the human-readable name for the field's current value. See @@ -620,6 +625,12 @@ Extra "help" text to be displayed under the field on the object's admin form. It's useful for documentation even if your object doesn't have an admin form. +Note that this value is *not* HTML-escaped when it's displayed in the admin +interface. This lets you include HTML in ``help_text`` if you so desire. For +example:: + + help_text="Please use the following format: <em>YYYY-MM-DD</em>." + ``primary_key`` ~~~~~~~~~~~~~~~ @@ -698,11 +709,11 @@ it using the field's attribute name, converting underscores to spaces. In this example, the verbose name is ``"Person's first name"``:: - first_name = models.CharField("Person's first name", maxlength=30) + first_name = models.CharField("Person's first name", max_length=30) In this example, the verbose name is ``"first name"``:: - first_name = models.CharField(maxlength=30) + first_name = models.CharField(max_length=30) ``ForeignKey``, ``ManyToManyField`` and ``OneToOneField`` require the first argument to be a model class, so use the ``verbose_name`` keyword argument:: @@ -727,7 +738,7 @@ Many-to-one relationships To define a many-to-one relationship, use ``ForeignKey``. You use it just like any other ``Field`` type: by including it as a class attribute of your model. -``ForeignKey`` requires a positional argument: The class to which the model is +``ForeignKey`` requires a positional argument: the class to which the model is related. For example, if a ``Car`` model has a ``Manufacturer`` -- that is, a @@ -775,7 +786,7 @@ You can, of course, call the field whatever you want. For example:: See the `Many-to-one relationship model example`_ for a full example. -.. _Many-to-one relationship model example: http://www.djangoproject.com/documentation/models/many_to_one/ +.. _Many-to-one relationship model example: ../models/many_to_one/ ``ForeignKey`` fields take a number of extra arguments for defining how the relationship should work. All are optional: @@ -862,7 +873,7 @@ To define a many-to-many relationship, use ``ManyToManyField``. You use it just like any other ``Field`` type: by including it as a class attribute of your model. -``ManyToManyField`` requires a positional argument: The class to which the +``ManyToManyField`` requires a positional argument: the class to which the model is related. For example, if a ``Pizza`` has multiple ``Topping`` objects -- that is, a @@ -902,7 +913,7 @@ set up above, the ``Pizza`` admin form would let users select the toppings. See the `Many-to-many relationship model example`_ for a full example. -.. _Many-to-many relationship model example: http://www.djangoproject.com/documentation/models/many_to_many/ +.. _Many-to-many relationship model example: ../models/many_to_many/ ``ManyToManyField`` objects take a number of extra arguments for defining how the relationship should work. All are optional: @@ -959,7 +970,7 @@ model. This is most useful on the primary key of an object when that object "extends" another object in some way. -``OneToOneField`` requires a positional argument: The class to which the +``OneToOneField`` requires a positional argument: the class to which the model is related. For example, if you're building a database of "places", you would build pretty @@ -979,7 +990,116 @@ as a read-only field when you edit an object in the admin interface: See the `One-to-one relationship model example`_ for a full example. -.. _One-to-one relationship model example: http://www.djangoproject.com/documentation/models/one_to_one/ +.. _One-to-one relationship model example: ../models/one_to_one/ + +Custom field types +------------------ + +**New in Django development version** + +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 + +.. admonition:: Experimental territory + + This is an area of Django that traditionally has not been documented, but + we're starting to include bits of documentation, one feature at a time. + Please forgive the sparseness of this section. + + If you like living on the edge and are comfortable with the risk of + unstable, undocumented APIs, see the code for the core ``Field`` class + in ``django/db/models/fields/__init__.py`` -- but if/when the innards + change, don't say we didn't warn you. + +To create a custom field type, simply subclass ``django.db.models.Field``. +Here is an incomplete list of the methods you should implement: + +``db_type()`` +~~~~~~~~~~~~~ + +Returns the database column data type for the ``Field``, taking into account +the current ``DATABASE_ENGINE`` setting. + +Say you've created a PostgreSQL custom type called ``mytype``. You can use this +field with Django by subclassing ``Field`` and implementing the ``db_type()`` +method, like so:: + + from django.db import models + + class MytypeField(models.Field): + def db_type(self): + 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 +import the Django settings module and check the ``DATABASE_ENGINE`` setting. +For example:: + + class MyDateField(models.Field): + def db_type(self): + from django.conf import settings + if settings.DATABASE_ENGINE == 'mysql': + return 'datetime' + else: + return 'timestamp' + +The ``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 ``DATABASE_ENGINE`` 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): + 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 +``__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): + return 'char(%s)' % self.max_length + + # In the model: + class MyModel(models.Model): + # ... + my_field = BetterCharField(25) + +Note that if you implement ``__init__()`` on a ``Field`` subclass, it's +important to call ``Field.__init__()`` -- i.e., the parent class' +``__init__()`` method. Meta options ============ @@ -987,7 +1107,7 @@ Meta options Give your model metadata by using an inner ``class Meta``, like so:: class Foo(models.Model): - bar = models.CharField(maxlength=30) + bar = models.CharField(max_length=30) class Meta: # ... @@ -1077,7 +1197,7 @@ See `Specifying ordering`_ for more examples. Note that, regardless of how many fields are in ``ordering``, the admin site uses only the first field. -.. _Specifying ordering: http://www.djangoproject.com/documentation/models/ordering/ +.. _Specifying ordering: ../models/ordering/ ``permissions`` --------------- @@ -1161,8 +1281,8 @@ If you want your model to be visible to Django's admin site, give your model an inner ``"class Admin"``, like so:: class Person(models.Model): - first_name = models.CharField(maxlength=30) - last_name = models.CharField(maxlength=30) + first_name = models.CharField(max_length=30) + last_name = models.CharField(max_length=30) class Admin: # Admin options go here @@ -1302,8 +1422,8 @@ that displays the ``__str__()`` representation of each object. A few special cases to note about ``list_display``: - * If the field is a ``ForeignKey``, Django will display the ``__str__()`` - of the related object. + * If the field is a ``ForeignKey``, Django will display the + ``__unicode__()`` of the related object. * ``ManyToManyField`` fields aren't supported, because that would entail executing a separate SQL statement for each row in the table. If you @@ -1321,7 +1441,7 @@ A few special cases to note about ``list_display``: Here's a full example model:: class Person(models.Model): - name = models.CharField(maxlength=50) + name = models.CharField(max_length=50) birthday = models.DateField() class Admin: @@ -1338,9 +1458,9 @@ A few special cases to note about ``list_display``: Here's a full example model:: class Person(models.Model): - first_name = models.CharField(maxlength=50) - last_name = models.CharField(maxlength=50) - color_code = models.CharField(maxlength=6) + first_name = models.CharField(max_length=50) + last_name = models.CharField(max_length=50) + color_code = models.CharField(max_length=6) class Admin: list_display = ('first_name', 'last_name', 'colored_name') @@ -1356,7 +1476,7 @@ A few special cases to note about ``list_display``: Here's a full example model:: class Person(models.Model): - first_name = models.CharField(maxlength=50) + first_name = models.CharField(max_length=50) birthday = models.DateField() class Admin: @@ -1367,10 +1487,11 @@ A few special cases to note about ``list_display``: born_in_fifties.boolean = True - * The ``__str__()`` method is just as valid in ``list_display`` as any - other model method, so it's perfectly OK to do this:: + * The ``__str__()`` and ``__unicode__()`` methods are just as valid in + ``list_display`` as any other model method, so it's perfectly OK to do + this:: - list_display = ('__str__', 'some_other_field') + list_display = ('__unicode__', 'some_other_field') * Usually, elements of ``list_display`` that aren't actual database fields can't be used in sorting (because Django does all the sorting at the @@ -1383,8 +1504,8 @@ A few special cases to note about ``list_display``: For example:: class Person(models.Model): - first_name = models.CharField(maxlength=50) - color_code = models.CharField(maxlength=6) + first_name = models.CharField(max_length=50) + color_code = models.CharField(max_length=6) class Admin: list_display = ('first_name', 'colored_first_name') @@ -1552,7 +1673,7 @@ with an operator: AND (first_name ILIKE 'lennon' OR last_name ILIKE 'lennon') Note that the query input is split by spaces, so, following this example, - it's not currently not possible to search for all records in which + it's currently not possible to search for all records in which ``first_name`` is exactly ``'john winston'`` (containing a space). ``@`` @@ -1634,13 +1755,13 @@ returns a list of all ``OpinionPoll`` objects, each with an extra return result_list class OpinionPoll(models.Model): - question = models.CharField(maxlength=200) + question = models.CharField(max_length=200) poll_date = models.DateField() objects = PollManager() class Response(models.Model): poll = models.ForeignKey(Poll) - person_name = models.CharField(maxlength=50) + person_name = models.CharField(max_length=50) response = models.TextField() With this example, you'd use ``OpinionPoll.objects.with_counts()`` to return @@ -1656,8 +1777,8 @@ A ``Manager``'s base ``QuerySet`` returns all objects in the system. For example, using this model:: class Book(models.Model): - title = models.CharField(maxlength=100) - author = models.CharField(maxlength=50) + title = models.CharField(max_length=100) + author = models.CharField(max_length=50) ...the statement ``Book.objects.all()`` will return all books in the database. @@ -1675,8 +1796,8 @@ all objects, and one that returns only the books by Roald Dahl:: # Then hook it into the Book model explicitly. class Book(models.Model): - title = models.CharField(maxlength=100) - author = models.CharField(maxlength=50) + title = models.CharField(max_length=100) + author = models.CharField(max_length=50) objects = models.Manager() # The default manager. dahl_objects = DahlBookManager() # The Dahl-specific manager. @@ -1709,9 +1830,9 @@ For example:: return super(FemaleManager, self).get_query_set().filter(sex='F') class Person(models.Model): - first_name = models.CharField(maxlength=50) - last_name = models.CharField(maxlength=50) - sex = models.CharField(maxlength=1, choices=(('M', 'Male'), ('F', 'Female'))) + first_name = models.CharField(max_length=50) + last_name = models.CharField(max_length=50) + sex = models.CharField(max_length=1, choices=(('M', 'Male'), ('F', 'Female'))) people = models.Manager() men = MaleManager() women = FemaleManager() @@ -1741,11 +1862,11 @@ model. For example, this model has a few custom methods:: class Person(models.Model): - first_name = models.CharField(maxlength=50) - last_name = models.CharField(maxlength=50) + first_name = models.CharField(max_length=50) + last_name = models.CharField(max_length=50) birth_date = models.DateField() - address = models.CharField(maxlength=100) - city = models.CharField(maxlength=50) + address = models.CharField(max_length=100) + city = models.CharField(max_length=50) state = models.USStateField() # Yes, this is America-centric... def baby_boomer_status(self): @@ -1776,20 +1897,47 @@ A few object methods have special meaning: ----------- ``__str__()`` is a Python "magic method" that defines what should be returned -if you call ``str()`` on the object. Django uses ``str(obj)`` in a number of -places, most notably as the value displayed to render an object in the Django -admin site and as the value inserted into a template when it displays an -object. Thus, you should always return a nice, human-readable string for the -object's ``__str__``. Although this isn't required, it's strongly encouraged. +if you call ``str()`` on the object. Django uses ``str(obj)`` (or the related +function, ``unicode(obj)`` -- see below) in a number of places, most notably +as the value displayed to render an object in the Django admin site and as the +value inserted into a template when it displays an object. Thus, you should +always return a nice, human-readable string for the object's ``__str__``. +Although this isn't required, it's strongly encouraged (see the description of +``__unicode__``, below, before putting ``_str__`` methods everywhere). For example:: class Person(models.Model): - first_name = models.CharField(maxlength=50) - last_name = models.CharField(maxlength=50) + first_name = models.CharField(max_length=50) + last_name = models.CharField(max_length=50) def __str__(self): - return '%s %s' % (self.first_name, self.last_name) + # Note use of django.utils.encoding.smart_str() here because + # first_name and last_name will be unicode strings. + return smart_str('%s %s' % (self.first_name, self.last_name)) + +``__unicode__`` +--------------- + +The ``__unicode__()`` method is called whenever you call ``unicode()`` on an +object. Since Django's database backends will return Unicode strings in your +model's attributes, you would normally want to write a ``__unicode__()`` +method for your model. The example in the previous section could be written +more simply as:: + + class Person(models.Model): + first_name = models.CharField(max_length=50) + last_name = models.CharField(max_length=50) + + def __unicode__(self): + return u'%s %s' % (self.first_name, self.last_name) + +If you define a ``__unicode__()`` method on your model and not a ``__str__()`` +method, Django will automatically provide you with a ``__str__()`` that calls +``__unicode()__`` and then converts the result correctly to a UTF-8 encoded +string object. This is recommended development practice: define only +``__unicode__()`` and let Django take care of the conversion to string objects +when required. ``get_absolute_url`` -------------------- @@ -1805,10 +1953,12 @@ Django uses this in its admin interface. If an object defines link that will jump you directly to the object's public view, according to ``get_absolute_url()``. -Also, a couple of other bits of Django, such as the syndication-feed framework, +Also, a couple of other bits of Django, such as the `syndication feed framework`_, use ``get_absolute_url()`` as a convenience to reward people who've defined the method. +.. _syndication feed framework: ../syndication_feeds/ + It's good practice to use ``get_absolute_url()`` in templates, instead of hard-coding your objects' URLs. For example, this template code is bad:: @@ -1823,7 +1973,9 @@ But this template code is good:: characters (required by the URI spec, `RFC 2396`_) that have been URL-encoded, if necessary. Code and templates using ``get_absolute_url()`` should be able to use the result directly without needing to do any - further processing. + further processing. You may wish to use the + ``django.utils.encoding.iri_to_uri()`` function to help with this if you + are using unicode strings a lot. .. _RFC 2396: http://www.ietf.org/rfc/rfc2396.txt @@ -1841,7 +1993,7 @@ works out the correct full URL path using the URLconf, substituting the parameters you have given into the URL. For example, if your URLconf contained a line such as:: - (r'^/people/(\d+)/$', 'people.views.details'), + (r'^people/(\d+)/$', 'people.views.details'), ...your model could have a ``get_absolute_url`` method that looked like this:: @@ -1864,8 +2016,8 @@ Similarly, if you had a URLconf entry that looked like:: 'day': self.created.day}) get_absolute_url = permalink(get_absolute_url) -Notice that we specify an empty sequence for the second argument in this case, -because we only want to pass keyword arguments, not named arguments. +Notice that we specify an empty sequence for the second parameter in this case, +because we only want to pass keyword parameters, not positional ones. In this way, you're tying the model's absolute URL to the view that is used to display it, without repeating the URL information anywhere. You can still @@ -1917,7 +2069,7 @@ A classic use-case for overriding the built-in methods is if you want something to happen whenever you save an object. For example:: class Blog(models.Model): - name = models.CharField(maxlength=100) + name = models.CharField(max_length=100) tagline = models.TextField() def save(self): @@ -1928,7 +2080,7 @@ to happen whenever you save an object. For example:: You can also prevent saving:: class Blog(models.Model): - name = models.CharField(maxlength=100) + name = models.CharField(max_length=100) tagline = models.TextField() def save(self): diff --git a/docs/modpython.txt b/docs/modpython.txt index 388a6168f3..c90296bd9a 100644 --- a/docs/modpython.txt +++ b/docs/modpython.txt @@ -50,8 +50,8 @@ 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 you've manually altered your ``PYTHONPATH`` to put your Django project -on it, you'll need to tell mod_python: +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:: @@ -63,6 +63,30 @@ on it, you'll need to tell mod_python: **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 ``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. + +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 +``DJANGO_SETTINGS_MODULE`` as in the above example. In this case, you would +need to write your ``PythonPath`` directive as:: + + PythonPath "['/var/production/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 +``/var/production/django-apps/weblog/`` to your ``PythonPath``. Remember: the +**parent directories** of anything you import directly must be on the Python +path. + .. caution:: If you're using Windows, remember that the path will contain backslashes. diff --git a/docs/newforms.txt b/docs/newforms.txt index 41db04a7dd..0b59a7ad65 100644 --- a/docs/newforms.txt +++ b/docs/newforms.txt @@ -641,7 +641,7 @@ the "Outputting forms as HTML" section above. The simplest way to display a form's HTML is to use the variable on its own, like this:: - <form method="post"> + <form method="post" action=""> <table>{{ form }}</table> <input type="submit" /> </form> @@ -653,7 +653,7 @@ class' ``__str__()`` method calls its ``as_table()`` method. The following is equivalent but a bit more explicit:: - <form method="post"> + <form method="post" action=""> <table>{{ form.as_table }}</table> <input type="submit" /> </form> @@ -675,10 +675,10 @@ individual fields for complete template control over the form's design. The easiest way is to iterate over the form's fields, with ``{% for field in form %}``. For example:: - <form method="post"> + <form method="post" action=""> <dl> {% for field in form %} - <dt>{{ field.label }}</dt> + <dt>{{ field.label_tag }}</dt> <dd>{{ field }}</dd> {% if field.help_text %}<dd>{{ field.help_text }}</dd>{% endif %} {% if field.errors %}<dd class="myerrors">{{ field.errors }}</dd>{% endif %} @@ -696,13 +696,13 @@ Alternatively, you can arrange the form's fields explicitly, by name. Do that by accessing ``{{ form.fieldname }}``, where ``fieldname`` is the field's name. For example:: - <form method="post"> + <form method="post" action=""> <ul class="myformclass"> - <li>{{ form.sender.label }} {{ form.sender }}</li> + <li>{{ form.sender.label_tag }} {{ form.sender }}</li> <li class="helptext">{{ form.sender.help_text }}</li> {% if form.sender.errors %}<ul class="errorlist">{{ form.sender.errors }}</ul>{% endif %} - <li>{{ form.subject.label }} {{ form.subject }}</li> + <li>{{ form.subject.label_tag }} {{ form.subject }}</li> <li class="helptext">{{ form.subject.help_text }}</li> {% if form.subject.errors %}<ul class="errorlist">{{ form.subject.errors }}</ul>{% endif %} @@ -710,6 +710,49 @@ For example:: </ul> </form> +Binding uploaded files to a form +-------------------------------- + +**New in Django development version** + +Dealing with forms that have ``FileField`` and ``ImageField`` fields +is a little more complicated than a normal form. + +Firstly, in order to upload files, you'll need to make sure that your +``<form>`` element correctly defines the ``enctype`` as +``"multipart/form-data"``:: + + <form enctype="multipart/form-data" method="post" action="/foo/"> + +Secondly, when you use the form, you need to bind the file data. File +data is handled separately to normal form data, so when your form +contains a ``FileField`` and ``ImageField``, you will need to specify +a second argument when you bind your form. So if we extend our +ContactForm to include an ``ImageField`` called ``mugshot``, we +need to bind the file data containing the mugshot image:: + + # Bound form with an image field + >>> data = {'subject': 'hello', + ... 'message': 'Hi there', + ... 'sender': 'foo@example.com', + ... 'cc_myself': True} + >>> file_data = {'mugshot': {'filename':'face.jpg' + ... 'content': <file data>}} + >>> f = ContactFormWithMugshot(data, file_data) + +In practice, you will usually specify ``request.FILES`` as the source +of file data (just like you use ``request.POST`` as the source of +form data):: + + # Bound form with an image field, data from the request + >>> f = ContactFormWithMugshot(request.POST, request.FILES) + +Constructing an unbound form is the same as always -- just omit both +form data *and* file data:: + + # Unbound form with a image field + >>> f = ContactFormWithMugshot() + Subclassing forms ----------------- @@ -919,7 +962,7 @@ validation if a particular field's value is not given. ``initial`` values are ~~~~~~~~~~ The ``widget`` argument lets you specify a ``Widget`` class to use when -rendering this ``Field``. See "Widgets" below for more information. +rendering this ``Field``. See "Widgets"_ below for more information. ``help_text`` ~~~~~~~~~~~~~ @@ -1086,6 +1129,24 @@ If no ``input_formats`` argument is provided, the default input formats are:: '%m/%d/%y %H:%M', # '10/25/06 14:30' '%m/%d/%y', # '10/25/06' +``DecimalField`` +~~~~~~~~~~~~~~~~ + +**New in Django development version** + + * Default widget: ``TextInput`` + * Empty value: ``None`` + * Normalizes to: A Python ``decimal``. + * Validates that the given value is a decimal. Leading and trailing + whitespace is ignored. + +Takes four optional arguments: ``max_value``, ``min_value``, ``max_digits``, +and ``decimal_places``. The first two define the limits for the fields value. +``max_digits`` is the maximum number of digits (those before the decimal +point plus those after the decimal point, with leading zeros stripped) +permitted in the value, whilst ``decimal_places`` is the maximum number of +decimal places permitted. + ``EmailField`` ~~~~~~~~~~~~~~ @@ -1099,6 +1160,54 @@ Has two optional arguments for validation, ``max_length`` and ``min_length``. If provided, these arguments ensure that the string is at most or at least the given length. +``FileField`` +~~~~~~~~~~~~~ + +**New in Django development version** + + * Default widget: ``FileInput`` + * Empty value: ``None`` + * Normalizes to: An ``UploadedFile`` object that wraps the file content + and file name into a single object. + * Validates that non-empty file data has been bound to the form. + +An ``UploadedFile`` object has two attributes: + + ====================== ===================================================== + Argument Description + ====================== ===================================================== + ``filename`` The name of the file, provided by the uploading + client. + ``content`` The array of bytes comprising the file content. + ====================== ===================================================== + +The string representation of an ``UploadedFile`` is the same as the filename +attribute. + +When you use a ``FileField`` on a form, you must also remember to +`bind the file data to the form`_. + +.. _`bind the file data to the form`: `Binding uploaded files to a form`_ + +``ImageField`` +~~~~~~~~~~~~~~ + +**New in Django development version** + + * Default widget: ``FileInput`` + * Empty value: ``None`` + * Normalizes to: An ``UploadedFile`` object that wraps the file content + and file name into a single object. + * Validates that file data has been bound to the form, and that the + file is of an image format understood by PIL. + +Using an ImageField requires that the `Python Imaging Library`_ is installed. + +When you use a ``FileField`` on a form, you must also remember to +`bind the file data to the form`_. + +.. _Python Imaging Library: http://www.pythonware.com/products/pil/ + ``IntegerField`` ~~~~~~~~~~~~~~~~ @@ -1108,6 +1217,9 @@ given length. * Validates that the given value is an integer. Leading and trailing whitespace is allowed, as in Python's ``int()`` function. +Takes two optional arguments for validation, ``max_value`` and ``min_value``. +These control the range of values permitted in the field. + ``MultipleChoiceField`` ~~~~~~~~~~~~~~~~~~~~~~~ @@ -1222,7 +1334,7 @@ Custom form and field validation Form validation happens when the data is cleaned. If you want to customise this process, there are various places you can change, each one serving a -different purpose. Thee types of cleaning methods are run during form +different purpose. Three types of cleaning methods are run during form processing. These are normally executed when you call the ``is_valid()`` method on a form. There are other things that can trigger cleaning and validation (accessing the ``errors`` attribute or calling ``full_clean()`` @@ -1307,12 +1419,12 @@ keep it simple and assume e-mail validation is contained in a function called class MultiEmailField(forms.Field): def clean(self, value): + if not value: + raise forms.ValidationError('Enter at least one e-mail address.') emails = value.split(',') for email in emails: if not is_valid_email(email): raise forms.ValidationError('%s is not a valid e-mail address.' % email) - if not emails: - raise forms.ValidationError('Enter at least one e-mail address.') return emails Let's alter the ongoing ``ContactForm`` example to demonstrate how you'd use @@ -1325,6 +1437,156 @@ like so:: senders = MultiEmailField() cc_myself = forms.BooleanField() +Widgets +======= + +A widget is Django's representation of a HTML input element. The widget +handles the rendering of the HTML, and the extraction of data from a GET/POST +dictionary that corresponds to the widget. + +Django provides a representation of all the basic HTML widgets, plus some +commonly used groups of widgets: + + ============================ =========================================== + Widget HTML Equivalent + ============================ =========================================== + ``TextInput`` ``<input type='text' ...`` + ``PasswordInput`` ``<input type='password' ...`` + ``HiddenInput`` ``<input type='hidden' ...`` + ``MultipleHiddenInput`` Multiple ``<input type='hidden' ...`` + instances. + ``FileInput`` ``<input type='file' ...`` + ``Textarea`` ``<textarea>...</textarea>`` + ``CheckboxInput`` ``<input type='checkbox' ...`` + ``Select`` ``<select><option ...`` + ``NullBooleanSelect`` Select widget with options 'Unknown', + 'Yes' and 'No' + ``SelectMultiple`` ``<select multiple='multiple'><option ...`` + ``RadioSelect`` ``<ul><li><input type='radio' ...`` + ``CheckboxSelectMultiple`` ``<ul><li><input type='checkbox' ...`` + ``MultiWidget`` Wrapper around multiple other widgets + ``SplitDateTimeWidget`` Wrapper around two ``TextInput`` widgets: + one for the Date, and one for the Time. + ============================ =========================================== + +Specifying widgets +------------------ + +Whenever you specify a field on a form, Django will use a default widget +that is appropriate to the type of data that is to be displayed. To find +which widget is used on which field, see the documentation for the +built-in Field classes. + +However, if you want to use a different widget for a field, you can - +just use the 'widget' argument on the field definition. For example:: + + class CommentForm(forms.Form): + name = forms.CharField() + url = forms.URLField() + comment = forms.CharField(widget=forms.Textarea) + +This would specify a form with a comment that uses a larger Textarea widget, +rather than the default TextInput widget. + +Customizing widget instances +---------------------------- + +When Django renders a widget as HTML, it only renders the bare minimum +HTML - Django doesn't add a class definition, or any other widget-specific +attributes. This means that all 'TextInput' widgets will appear the same +on your web page. + +If you want to make one widget look different to another, you need to +specify additional attributes for each widget. When you specify a +widget, you can provide a list of attributes that will be added to the +rendered HTML for the widget. + +For example, take the following simple form:: + + class CommentForm(forms.Form): + name = forms.CharField() + url = forms.URLField() + comment = forms.CharField() + +This form will include three default TextInput widgets, with default rendering - +no CSS class, no extra attributes. This means that the inputs boxes provided for +each widget will be rendered exactly the same:: + + >>> f = CommentForm(auto_id=False) + >>> f.as_table() + <tr><th>Name:</th><td><input type="text" name="name" /></td></tr> + <tr><th>Url:</th><td><input type="text" name="url"/></td></tr> + <tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr> + +On a real web page, you probably don't want every widget to look the same. You +might want a larger input element for the comment, and you might want the +'name' widget to have some special CSS class. To do this, you specify a +custom widget for your fields, and specify some attributes to use +when rendering those widgets:: + + class CommentForm(forms.Form): + name = forms.CharField( + widget=forms.TextInput(attrs={'class':'special'})) + url = forms.URLField() + comment = forms.CharField( + widget=forms.TextInput(attrs={'size':'40'})) + +Django will then include the extra attributes in the rendered output:: + + >>> f = CommentForm(auto_id=False) + >>> f.as_table() + <tr><th>Name:</th><td><input type="text" name="name" class="special"/></td></tr> + <tr><th>Url:</th><td><input type="text" name="url"/></td></tr> + <tr><th>Comment:</th><td><input type="text" name="comment" size="40"/></td></tr> + +Custom Widgets +-------------- + +When you start to write a lot of forms, you will probably find that you will +reuse certain sets of widget attributes over and over again. Rather than +repeat these attribute definitions every time you need them, Django allows +you to capture those definitions as a custom widget. + +For example, if you find that you are including a lot of comment fields on forms, +you could capture the idea of a ``TextInput`` with a specific ``size`` attribute +as a custom extension to the ``TextInput`` widget:: + + class CommentWidget(forms.TextInput): + def __init__(self, *args, **kwargs): + kwargs.setdefault('attrs',{}).update({'size': '40'}) + super(forms.TextInput, self).__init__(*args, **kwargs) + +Then you can use this widget in your forms:: + + class CommentForm(forms.Form): + name = forms.CharField() + url = forms.URLField() + comment = forms.CharField(widget=CommentWidget) + +You can even customize your custom widget, in the same way as you would +any other widget. Adding a once-off class to your ``CommentWidget`` is as +simple as adding an attribute definition:: + + class CommentForm(forms.Form): + name = forms.CharField(max_length=20) + url = forms.URLField() + comment = forms.CharField( + widget=CommentWidget(attrs={'class': 'special'})) + +Django also makes it easy to specify a custom field type that uses your custom +widget. For example, you could define a customized field type for comments +by defining:: + + class CommentInput(forms.CharField): + widget = CommentWidget + +You can then use this field whenever you have a form that requires a comment:: + + class CommentForm(forms.Form): + name = forms.CharField() + url = forms.URLField() + comment = CommentInput() + Generating forms for models =========================== @@ -1372,17 +1634,17 @@ the full list of conversions: ``AutoField`` Not represented in the form ``BooleanField`` ``BooleanField`` ``CharField`` ``CharField`` with ``max_length`` set to - the model field's ``maxlength`` + the model field's ``max_length`` ``CommaSeparatedIntegerField`` ``CharField`` ``DateField`` ``DateField`` ``DateTimeField`` ``DateTimeField`` ``DecimalField`` ``DecimalField`` ``EmailField`` ``EmailField`` - ``FileField`` ``CharField`` + ``FileField`` ``FileField`` ``FilePathField`` ``CharField`` ``FloatField`` ``FloatField`` ``ForeignKey`` ``ModelChoiceField`` (see below) - ``ImageField`` ``CharField`` + ``ImageField`` ``ImageField`` ``IntegerField`` ``IntegerField`` ``IPAddressField`` ``CharField`` ``ManyToManyField`` ``ModelMultipleChoiceField`` (see @@ -1452,15 +1714,15 @@ Consider this set of models:: ) class Author(models.Model): - name = models.CharField(maxlength=100) - title = models.CharField(maxlength=3, choices=TITLE_CHOICES) + name = models.CharField(max_length=100) + title = models.CharField(max_length=3, choices=TITLE_CHOICES) birth_date = models.DateField(blank=True, null=True) - def __str__(self): + def __unicode__(self): return self.name class Book(models.Model): - name = models.CharField(maxlength=100) + name = models.CharField(max_length=100) authors = models.ManyToManyField(Author) With these models, a call to ``form_for_model(Author)`` would return a ``Form`` @@ -1495,6 +1757,51 @@ example:: Note that ``save()`` will raise a ``ValueError`` if the data in the form doesn't validate -- i.e., ``if form.errors``. +This ``save()`` method accepts an optional ``commit`` keyword argument, which +accepts either ``True`` or ``False``. If you call ``save()`` with +``commit=False``, then it will return an object that hasn't yet been saved to +the database. In this case, it's up to you to call ``save()`` on the resulting +model instance. This is useful if you want to do custom processing on the +object before saving it. ``commit`` is ``True`` by default. + +Another side effect of using ``commit=False`` is seen when your model has +a many-to-many relation with another model. If your model has a many-to-many +relation and you specify ``commit=False`` when you save a form, Django cannot +immediately save the form data for the many-to-many relation. This is because +it isn't possible to save many-to-many data for an instance until the instance +exists in the database. + +To work around this problem, every time you save a form using ``commit=False``, +Django adds a ``save_m2m()`` method to the form created by ``form_for_model``. +After you've manually saved the instance produced by the form, you can invoke +``save_m2m()`` to save the many-to-many form data. For example:: + + # Create a form instance with POST data. + >>> f = AuthorForm(request.POST) + + # Create, but don't save the new author instance. + >>> new_author = f.save(commit=False) + + # Modify the author in some way. + >>> new_author.some_field = 'some_value' + + # Save the new instance. + >>> new_author.save() + + # Now, save the many-to-many data for the form. + >>> f.save_m2m() + +Calling ``save_m2m()`` is only required if you use ``save(commit=False)``. +When you use a simple ``save()`` on a form, all data -- including +many-to-many data -- is saved without the need for any additional method calls. +For example:: + + # Create a form instance with POST data. + >>> f = AuthorForm(request.POST) + + # Create and save the new author instance. There's no need to do anything else. + >>> new_author = f.save() + Using an alternate base class ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1624,6 +1931,42 @@ will raise ``ValueError`` if the data doesn't validate. ``form_for_instance()`` has ``form``, ``fields`` and ``formfield_callback`` arguments that behave the same way as they do for ``form_for_model()``. +Let's modify the earlier `contact form`_ view example a little bit. Suppose we +have a ``Message`` model that holds each contact submission. Something like:: + + class Message(models.Model): + subject = models.CharField(max_length=100) + message = models.TextField() + sender = models.EmailField() + cc_myself = models.BooleanField() + +You could use this model to create a form (using ``form_for_model()``). You +could also use existing ``Message`` instances to create a form for editing +messages. The earlier_ view can be changed slightly to accept the ``id`` value +of an existing ``Message`` and present it for editing:: + + def contact_edit(request, msg_id): + # Create the form from the message id. + message = get_object_or_404(Message, id=msg_id) + ContactForm = form_for_instance(message) + + if request.method == 'POST': + form = ContactForm(request.POST) + if form.is_valid(): + form.save() + return HttpResponseRedirect('/url/on_success/') + else: + form = ContactForm() + return render_to_response('contact.html', {'form': form}) + +Aside from how we create the ``ContactForm`` class here, the main point to +note is that the form display in the ``GET`` branch of the function +will use the values from the ``message`` instance as initial values for the +form field. + +.. _contact form: `Simple view example`_ +.. _earlier: `Simple view example`_ + When should you use ``form_for_model()`` and ``form_for_instance()``? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/overview.txt b/docs/overview.txt index 7b3559663a..0fd1be5c85 100644 --- a/docs/overview.txt +++ b/docs/overview.txt @@ -25,18 +25,18 @@ far, it's been solving two years' worth of database-schema problems. Here's a quick example:: class Reporter(models.Model): - full_name = models.CharField(maxlength=70) + full_name = models.CharField(max_length=70) - def __str__(self): + def __unicode__(self): return self.full_name class Article(models.Model): pub_date = models.DateTimeField() - headline = models.CharField(maxlength=200) + headline = models.CharField(max_length=200) article = models.TextField() reporter = models.ForeignKey(Reporter) - def __str__(self): + def __unicode__(self): return self.headline Install it @@ -54,7 +54,7 @@ Enjoy the free API ================== With that, you've got a free, and rich, Python API to access your data. The API -is created on the fly: No code generation necessary:: +is created on the fly, no code generation necessary:: >>> from mysite.models import Reporter, Article @@ -124,7 +124,7 @@ is created on the fly: No code generation necessary:: # Delete an object with delete(). >>> r.delete() -A dynamic admin interface: It's not just scaffolding -- it's the whole house +A dynamic admin interface: it's not just scaffolding -- it's the whole house ============================================================================ Once your models are defined, Django can automatically create a professional, @@ -134,7 +134,7 @@ your model classes:: class Article(models.Model): pub_date = models.DateTimeField() - headline = models.CharField(maxlength=200) + headline = models.CharField(max_length=200) article = models.TextField() reporter = models.ForeignKey(Reporter) class Admin: pass @@ -165,9 +165,9 @@ example above:: from django.conf.urls.defaults import * urlpatterns = patterns('', - (r'^/articles/(\d{4})/$', 'mysite.views.year_archive'), - (r'^/articles/(\d{4})/(\d{2})/$', 'mysite.views.month_archive'), - (r'^/articles/(\d{4})/(\d{2})/(\d+)/$', 'mysite.views.article_detail'), + (r'^articles/(\d{4})/$', 'mysite.views.year_archive'), + (r'^articles/(\d{4})/(\d{2})/$', 'mysite.views.month_archive'), + (r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'mysite.views.article_detail'), ) The code above maps URLs, as simple regular expressions, to the location of @@ -250,7 +250,7 @@ Finally, Django uses the concept of "template inheritance": That's what the ``{% extends "base.html" %}`` does. It means "First load the template called 'base', which has defined a bunch of blocks, and fill the blocks with the following blocks." In short, that lets you dramatically cut down on redundancy -in templates: Each template has to define only what's unique to that template. +in templates: each template has to define only what's unique to that template. Here's what the "base.html" template might look like:: @@ -288,14 +288,16 @@ This has been only a quick overview of Django's functionality. Some more useful features: * A caching framework that integrates with memcached or other backends. - * A syndication framework that makes creating RSS and Atom feeds as easy as + * A `syndication framework`_ that makes creating RSS and Atom feeds as easy as writing a small Python class. * More sexy automatically-generated admin features -- this overview barely scratched the surface. +.. _syndication framework: ../syndication_feeds/ + The next obvious steps are for you to `download Django`_, read `the tutorial`_ and join `the community`_. Thanks for your interest! .. _download Django: http://www.djangoproject.com/download/ -.. _the tutorial: http://www.djangoproject.com/documentation/tutorial01/ +.. _the tutorial: ../tutorial01/ .. _the community: http://www.djangoproject.com/community/ diff --git a/docs/release_notes_0.95.txt b/docs/release_notes_0.95.txt index 3709cacf5a..f2ecbb66e6 100644 --- a/docs/release_notes_0.95.txt +++ b/docs/release_notes_0.95.txt @@ -114,7 +114,7 @@ there's a #django channel on irc.freenode.net that is regularly populated by Django users and developers from around the world. Friendly people are usually available at any hour of the day -- to help, or just to chat. -.. _online: http://www.djangoproject.com/documentation/ +.. _online: http://www.djangoproject.com/documentation/0.95/ .. _Django website: http://www.djangoproject.com/ .. _FAQ: http://www.djangoproject.com/documentation/faq/ .. _django-users: http://groups.google.com/group/django-users diff --git a/docs/request_response.txt b/docs/request_response.txt index 0b985d563b..867464226a 100644 --- a/docs/request_response.txt +++ b/docs/request_response.txt @@ -38,6 +38,16 @@ All attributes except ``session`` should be considered read-only. elif request.method == 'POST': do_something_else() +``encoding`` + **New in Django development version** + + A string representing the current encoding used to decode form submission + data (or ``None``, which means the ``DEFAULT_CHARSET`` setting is used). + You can write to this attribute to change the encoding used when accessing + the form data. Any subsequent attribute accesses (such as reading from + ``GET`` or ``POST``) will use the new ``encoding`` value. Useful if you + know the form data is not in the ``DEFAULT_CHARSET`` encoding. + ``GET`` A dictionary-like object containing all given HTTP GET parameters. See the ``QueryDict`` documentation below. @@ -297,7 +307,7 @@ In contrast to ``HttpRequest`` objects, which are created automatically by Django, ``HttpResponse`` objects are your responsibility. Each view you write is responsible for instantiating, populating and returning an ``HttpResponse``. -The ``HttpResponse`` class lives at ``django.http.HttpResponse``. +The ``HttpResponse`` class lives in the ``django.http`` module. Usage ----- @@ -342,7 +352,7 @@ hard-coded strings. If you use this technique, follow these guidelines: Methods ------- -``__init__(content='', mimetype=DEFAULT_CONTENT_TYPE)`` +``__init__(content='', mimetype=None, status=200, content_type=DEFAULT_CONTENT_TYPE)`` Instantiates an ``HttpResponse`` object with the given page content (a string) and MIME type. The ``DEFAULT_CONTENT_TYPE`` is ``'text/html'``. @@ -350,6 +360,16 @@ Methods return strings, and those strings will be joined together to form the content of the response. + ``status`` is the `HTTP Status code`_ for the response. + + **(New in Django development version)** ``content_type`` is an alias for + ``mimetype``. Historically, the parameter was only called ``mimetype``, + but since this is actually the value included in the HTTP ``Content-Type`` + header, it can also include the character set encoding, which makes it + more than just a MIME type specification. If ``mimetype`` is specifiedi + (not None), that value is used. Otherwise, ``content_type`` is used. If + neither is given, the ``DEFAULT_CONTENT_TYPE`` setting is used. + ``__setitem__(header, value)`` Sets the given header name to the given value. Both ``header`` and ``value`` should be strings. @@ -396,6 +416,8 @@ Methods ``write(content)``, ``flush()`` and ``tell()`` These methods make an ``HttpResponse`` instance a file-like object. +.. _HTTP Status code: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10 + HttpResponse subclasses ----------------------- diff --git a/docs/sessions.txt b/docs/sessions.txt index c7124ba703..96a88c617a 100644 --- a/docs/sessions.txt +++ b/docs/sessions.txt @@ -190,6 +190,12 @@ deleted:: # request.session['foo'] instead of request.session. request.session['foo']['bar'] = 'baz' +In the last case of the above example, we can tell the session object +explicitly that it has been modified by setting the ``modified`` attribute on +the session object:: + + request.session.modified = True + To change this default behavior, set the ``SESSION_SAVE_EVERY_REQUEST`` setting to ``True``. If ``SESSION_SAVE_EVERY_REQUEST`` is ``True``, Django will save the session to the database on every single request. diff --git a/docs/settings.txt b/docs/settings.txt index 897cdc8099..050e377713 100644 --- a/docs/settings.txt +++ b/docs/settings.txt @@ -233,12 +233,21 @@ Default: ``'simple://'`` The cache backend to use. See the `cache docs`_. CACHE_MIDDLEWARE_KEY_PREFIX +--------------------------- Default: ``''`` (Empty string) The cache key prefix that the cache middleware should use. See the `cache docs`_. +CACHE_MIDDLEWARE_SECONDS +------------------------ + +Default: ``600`` + +The default number of seconds to cache a page when the caching middleware or +``cache_page()`` decorator is used. + DATABASE_ENGINE --------------- @@ -442,6 +451,16 @@ Default: ``False`` Whether to use a TLS (secure) connection when talking to the SMTP server. +FILE_CHARSET +------------ + +**New in Django development version** + +Default: ``'utf-8'`` + +The character encoding used to decode any files read from disk. This includes +template files and initial SQL data files. + FIXTURE_DIRS ------------- diff --git a/docs/sitemaps.txt b/docs/sitemaps.txt index 550f448de1..1d4fba2626 100644 --- a/docs/sitemaps.txt +++ b/docs/sitemaps.txt @@ -21,7 +21,7 @@ you express this information in Python code. It works much like Django's `syndication framework`_. To create a sitemap, just write a ``Sitemap`` class and point to it in your URLconf_. -.. _syndication framework: ../syndication/ +.. _syndication framework: ../syndication_feeds/ .. _URLconf: ../url_dispatch/ Installation diff --git a/docs/sites.txt b/docs/sites.txt index 12259b04c3..90a9d0f90f 100644 --- a/docs/sites.txt +++ b/docs/sites.txt @@ -46,7 +46,7 @@ that's represented by a ``ManyToManyField`` in the ``Article`` model:: from django.contrib.sites.models import Site class Article(models.Model): - headline = models.CharField(maxlength=200) + headline = models.CharField(max_length=200) # ... sites = models.ManyToManyField(Site) @@ -87,7 +87,7 @@ like this:: from django.contrib.sites.models import Site class Article(models.Model): - headline = models.CharField(maxlength=200) + headline = models.CharField(max_length=200) # ... site = models.ForeignKey(Site) @@ -229,7 +229,7 @@ Use ``CurrentSiteManager`` by adding it to your model explicitly. For example:: class Photo(models.Model): photo = models.FileField(upload_to='/home/photos') - photographer_name = models.CharField(maxlength=100) + photographer_name = models.CharField(max_length=100) pub_date = models.DateField() site = models.ForeignKey(Site) objects = models.Manager() @@ -257,7 +257,7 @@ this:: class Photo(models.Model): photo = models.FileField(upload_to='/home/photos') - photographer_name = models.CharField(maxlength=100) + photographer_name = models.CharField(max_length=100) pub_date = models.DateField() publish_on = models.ForeignKey(Site) objects = models.Manager() @@ -318,5 +318,23 @@ Here's how Django uses the sites framework: .. _redirects framework: ../redirects/ .. _flatpages framework: ../flatpages/ -.. _syndication framework: ../syndication/ +.. _syndication framework: ../syndication_feeds/ .. _authentication framework: ../authentication/ + +``RequestSite`` objects +======================= + +**New in Django development version** + +Some ``django.contrib`` applications take advantage of the sites framework but +are architected in a way that doesn't *require* the sites framework to be +installed in your database. (Some people don't want to, or just aren't *able* +to install the extra database table that the sites framework requires.) For +those cases, the framework provides a ``RequestSite`` class, which can be used +as a fallback when the database-backed sites framework is not available. + +A ``RequestSite`` object has a similar interface to a normal ``Site`` object, +except its ``__init__()`` method takes an ``HttpRequest`` object. It's able to +deduce the ``domain`` and ``name`` by looking at the request's domain. It has +``save()`` and ``delete()`` methods to match the interface of ``Site``, but +the methods raise ``NotImplementedError``. diff --git a/docs/static_files.txt b/docs/static_files.txt index b6a1d278fd..846c3eb56b 100644 --- a/docs/static_files.txt +++ b/docs/static_files.txt @@ -103,10 +103,10 @@ Do this by wrapping an ``if DEBUG`` statement around the 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'), + (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: diff --git a/docs/syndication_feeds.txt b/docs/syndication_feeds.txt index 2a03e6d5a6..393572f3e2 100644 --- a/docs/syndication_feeds.txt +++ b/docs/syndication_feeds.txt @@ -31,6 +31,12 @@ To create a feed, just write a ``Feed`` class and point to it in your URLconf_. Initialization -------------- +If you're not using the latest Django development version, you'll need to make +sure Django's sites framework is installed -- including its database table. +(See the `sites framework documentation`_ for more information.) This has +changed in the Django development version; the syndication feed framework no +longer requires the sites framework. + To activate syndication feeds on your Django site, add this line to your URLconf_:: @@ -72,6 +78,7 @@ The above example registers two feeds: Once that's set up, you just need to define the ``Feed`` classes themselves. +.. _sites framework documentation: ../sites/ .. _URLconf: ../url_dispatch/ .. _settings file: ../settings/ @@ -131,9 +138,14 @@ put into those elements. * ``{{ obj }}`` -- The current object (one of whichever objects you returned in ``items()``). - * ``{{ site }}`` -- A ``django.models.core.sites.Site`` object + * ``{{ site }}`` -- A ``django.contrib.sites.models.Site`` object representing the current site. This is useful for - ``{{ site.domain }}`` or ``{{ site.name }}``. + ``{{ site.domain }}`` or ``{{ site.name }}``. Note that if you're + using the latest Django development version and do *not* have the + Django sites framework installed, this will be set to a + ``django.contrib.sites.models.RequestSite`` object. See the + `RequestSite section of the sites framework documentation`_ for + more. If you don't create a template for either the title or description, the framework will use the template ``"{{ obj }}"`` by default -- that is, @@ -164,6 +176,7 @@ put into those elements. .. _chicagocrime.org: http://www.chicagocrime.org/ .. _object-relational mapper: ../db-api/ .. _Django templates: ../templates/ +.. _RequestSite section of the sites framework documentation: ../sites/#requestsite-objects A complex example ----------------- @@ -416,6 +429,26 @@ This example illustrates all possible attributes and methods for a ``Feed`` clas link = '/foo/bar/' # Hard-coded link. + # GUID -- One of the following three is optional. The framework looks + # for them in this order. This property is only used for Atom feeds + # (where it is the feed-level ID element). If not provided, the feed + # link is used as the ID. + # + # (New in Django development version) + + def feed_guid(self, obj): + """ + Takes the object returned by get_object() and returns the globally + unique ID for the feed as a normal Python string. + """ + + def feed_guid(self): + """ + Returns the feed's globally unique ID as a normal Python string. + """ + + feed_guid = '/foo/bar/1234' # Hard-coded guid. + # DESCRIPTION -- One of the following three is required. The framework # looks for them in this order. @@ -556,7 +589,18 @@ This example illustrates all possible attributes and methods for a ``Feed`` clas Returns the URL for every item in the feed. """ - # ITEM AUTHOR NAME --One of the following three is optional. The + # ITEM_GUID -- The following method is optional. This property is + # only used for Atom feeds (it is the ID element for an item in an + # Atom feed). If not provided, the item's link is used by default. + # + # (New in Django development version) + + def item_guid(self, obj): + """ + Takes an item, as return by items(), and returns the item's ID. + """ + + # ITEM AUTHOR NAME -- One of the following three is optional. The # framework looks for them in this order. def item_author_name(self, item): diff --git a/docs/templates.txt b/docs/templates.txt index c32b1af1dd..6cebd3b7bd 100644 --- a/docs/templates.txt +++ b/docs/templates.txt @@ -741,8 +741,19 @@ regroup Regroup a list of alike objects by a common attribute. This complex tag is best illustrated by use of an example: say that ``people`` -is a list of ``Person`` objects that have ``first_name``, ``last_name``, and -``gender`` attributes, and you'd like to display a list that looks like: +is a list of people represented by dictionaries with ``first_name``, +``last_name``, and ``gender`` keys:: + + people = [ + {'first_name': 'George', 'last_name': 'Bush', 'gender': 'Male'}, + {'first_name': 'Bill', 'last_name': 'Clinton', 'gender': 'Male'}, + {'first_name': 'Margaret', 'last_name': 'Thatcher', 'gender': 'Female'}, + {'first_name': 'Condoleezza', 'last_name': 'Rice', 'gender': 'Female'}, + {'first_name': 'Pat', 'last_name': 'Smith', 'gender': 'Unknown'}, + ] + +...and you'd like to display a hierarchical list that is ordered by gender, +like this: * Male: * George Bush @@ -753,33 +764,72 @@ is a list of ``Person`` objects that have ``first_name``, ``last_name``, and * Unknown: * Pat Smith -The following snippet of template code would accomplish this dubious task:: +You can use the ``{% regroup %}`` tag to group the list of people by gender. +The following snippet of template code would accomplish this:: + + {% regroup people by gender as gender_list %} - {% regroup people by gender as grouped %} <ul> - {% for group in grouped %} - <li>{{ group.grouper }} + {% for gender in gender_list %} + <li>{{ gender.grouper }} <ul> - {% for item in group.list %} - <li>{{ item }}</li> + {% for item in gender.list %} + <li>{{ item.first_name }} {{ item.last_name }}</li> {% endfor %} </ul> </li> {% endfor %} </ul> -As you can see, ``{% regroup %}`` populates a variable with a list of objects -with ``grouper`` and ``list`` attributes. ``grouper`` contains the item that -was grouped by; ``list`` contains the list of objects that share that -``grouper``. In this case, ``grouper`` would be ``Male``, ``Female`` and -``Unknown``, and ``list`` is the list of people with those genders. +Let's walk through this example. ``{% regroup %}`` takes three arguments: the +list you want to regroup, the attribute to group by, and the name of the +resulting list. Here, we're regrouping the ``people`` list by the ``gender`` +attribute and calling the result ``gender_list``. + +``{% regroup %}`` produces a list (in this case, ``gender_list``) of +**group objects**. Each group object has two attributes: + + * ``grouper`` -- the item that was grouped by (e.g., the string "Male" or + "Female"). + * ``list`` -- a list of all items in this group (e.g., a list of all people + with gender='Male'). + +Note that ``{% regroup %}`` does not order its input! Our example relies on +the fact that the ``people`` list was ordered by ``gender`` in the first place. +If the ``people`` list did *not* order its members by ``gender``, the regrouping +would naively display more than one group for a single gender. For example, +say the ``people`` list was set to this (note that the males are not grouped +together):: + + people = [ + {'first_name': 'Bill', 'last_name': 'Clinton', 'gender': 'Male'}, + {'first_name': 'Pat', 'last_name': 'Smith', 'gender': 'Unknown'}, + {'first_name': 'Margaret', 'last_name': 'Thatcher', 'gender': 'Female'}, + {'first_name': 'George', 'last_name': 'Bush', 'gender': 'Male'}, + {'first_name': 'Condoleezza', 'last_name': 'Rice', 'gender': 'Female'}, + ] + +With this input for ``people``, the example ``{% regroup %}`` template code +above would result in the following output: + + * Male: + * Bill Clinton + * Unknown: + * Pat Smith + * Female: + * Margaret Thatcher + * Male: + * George Bush + * Female: + * Condoleezza Rice + +The easiest solution to this gotcha is to make sure in your view code that the +data is ordered according to how you want to display it. -Note that ``{% regroup %}`` does not work when the list to be grouped is not -sorted by the key you are grouping by! This means that if your list of people -was not sorted by gender, you'd need to make sure it is sorted before using it, -i.e.:: +Another solution is to sort the data in the template using the ``dictsort`` +filter, if your data is in a list of dictionaries:: - {% regroup people|dictsort:"gender" by gender as grouped %} + {% regroup people|dictsort:"gender" by gender as gender_list %} spaceless ~~~~~~~~~ @@ -887,6 +937,12 @@ such as this:: The template tag will output the string ``/clients/client/123/``. +**New in development version:** If you're using `named URL patterns`_, +you can refer to the name of the pattern in the ``url`` tag instead of +using the path to the view. + +.. _named URL patterns: ../url_dispatch/#naming-url-patterns + widthratio ~~~~~~~~~~ @@ -950,7 +1006,7 @@ Removes all values of arg from the given string. date ~~~~ -Formats a date according to the given format (same as the ``now`` tag). +Formats a date according to the given format (same as the `now`_ tag). default ~~~~~~~ @@ -965,14 +1021,14 @@ If value is ``None``, use given default. dictsort ~~~~~~~~ -Takes a list of dicts, returns that list sorted by the property given in the -argument. +Takes a list of dictionaries, returns that list sorted by the key given in +the argument. dictsortreversed ~~~~~~~~~~~~~~~~ -Takes a list of dicts, returns that list sorted in reverse order by the -property given in the argument. +Takes a list of dictionaries, returns that list sorted in reverse order by the +key given in the argument. divisibleby ~~~~~~~~~~~ @@ -1040,6 +1096,16 @@ right-most digit, 2 is the second-right-most digit, etc. Returns the original value for invalid input (if input or argument is not an integer, or if argument is less than 1). Otherwise, output is always an integer. +iriencode +~~~~~~~~~ + +Converts an IRI (Internationalized Resource Identifier) to a string that is +suitable for including in a URL. This is necessary if you're trying to use +strings containing non-ASCII characters in a URL. + +It's safe to use this filter on a string that has already gone through the +``urlencode`` filter. + join ~~~~ @@ -1179,7 +1245,10 @@ Strips all [X]HTML tags. time ~~~~ -Formats a time according to the given format (same as the ``now`` tag). +Formats a time according to the given format (same as the `now`_ tag). +The time filter will only accept parameters in the format string that relate +to the time of day, not the date (for obvious reasons). If you need to +format a date, use the `date`_ filter. timesince ~~~~~~~~~ @@ -1263,12 +1332,17 @@ urlize Converts URLs in plain text into clickable links. +Note that if ``urlize`` is applied to text that already contains HTML markup, +things won't work as expected. Apply this filter only to *plain* text. + urlizetrunc ~~~~~~~~~~~ Converts URLs into clickable links, truncating URLs longer than the given character limit. +As with urlize_, this filter should only be applied to *plain* text. + **Argument:** Length to truncate URLs to wordcount diff --git a/docs/templates_python.txt b/docs/templates_python.txt index 7171f32612..261eaedf74 100644 --- a/docs/templates_python.txt +++ b/docs/templates_python.txt @@ -277,7 +277,7 @@ Subclassing Context: RequestContext Django comes with a special ``Context`` class, ``django.template.RequestContext``, that acts slightly differently than -the normal ``django.template.Context``. The first difference is that takes +the normal ``django.template.Context``. The first difference is that it takes an `HttpRequest object`_ as its first argument. For example:: c = RequestContext(request, { @@ -311,9 +311,10 @@ optional, third positional argument, ``processors``. In this example, the def some_view(request): # ... - return RequestContext(request, { + c = RequestContext(request, { 'foo': 'bar', }, [ip_address_processor]) + return t.render(c) Note:: If you're using Django's ``render_to_response()`` shortcut to populate a @@ -673,11 +674,11 @@ If you are writing a template filter which only expects a string as the first argument, you should use the included decorator ``stringfilter`` which will convert an object to it's string value before being passed to your function:: - from django import template + from django.template.defaultfilters import stringfilter - @template.stringfilter + @stringfilter def lower(value): - return value.lower() + return value.lower() Writing custom template tags ---------------------------- diff --git a/docs/testing.txt b/docs/testing.txt index b326e0099d..e27479cc34 100644 --- a/docs/testing.txt +++ b/docs/testing.txt @@ -22,6 +22,9 @@ it should be doing. The best part is, it's really easy. +This document is split into two primary sections. First, we explain how to +write tests with Django. Then, we explain how to run them. + .. admonition:: Note This testing framework is currently under development. It may change @@ -32,37 +35,83 @@ The best part is, it's really easy. Writing tests ============= -Tests in Django come in two forms: doctests and unit tests. +There are two primary ways to write tests with Django, corresponding to the +two test frameworks that ship in the Python standard library. The two +frameworks are: + + * **Doctests** -- tests that are embedded in your functions' docstrings and + are written in a way that emulates a session of the Python interactive + interpreter. For example:: + + def my_func(a_list, idx): + """ + >>> a = ['larry', 'curly', 'moe'] + >>> my_func(a, 0) + 'larry' + >>> my_func(a, 1) + 'curly' + """ + return a_list[idx] + + * **Unit tests** -- tests that are expressed as methods on a Python class + that subclasses ``unittest.TestCase``. For example:: + + import unittest + + class MyFuncTestCase(unittest.TestCase) + def testBasic(self): + a = ['larry', 'curly', 'moe'] + self.assertEquals(my_func(a, 0), 'larry') + self.assertEquals(my_func(a, 1), 'curly') + +You can choose the test framework you like, depending on which syntax you +prefer, or you can mix and match, using one framework for some of your code and +the other framework for other code. You can also use any *other* Python test +frameworks, as we'll explain in a bit. Writing doctests ---------------- -Doctests use Python's standard doctest_ module, which searches for tests in -your docstrings. Django's test runner looks for doctests in your ``models.py`` -file, and executes any that it finds. Django will also search for a file -called ``tests.py`` in the application directory (i.e., the directory that -holds ``models.py``). If a ``tests.py`` is found, it will also be searched -for doctests. +Doctests use Python's standard doctest_ module, which searches your docstrings +for statements that resemble a session of the Python interactive interpreter. +A full explanation of how doctest works is out of the scope of this document; +read Python's official documentation for the details. .. admonition:: What's a **docstring**? - A good explanation of docstrings (and some guidlines for using them + A good explanation of docstrings (and some guidelines for using them effectively) can be found in :PEP:`257`: A docstring is a string literal that occurs as the first statement in a module, function, class, or method definition. Such a docstring becomes the ``__doc__`` special attribute of that object. - Since tests often make great documentation, doctest lets you put your - tests directly in your docstrings. + For example, this function has a docstring that describes what it does:: -You can put doctest strings on any object in your ``models.py``, but it's -common practice to put application-level doctests in the module docstring, and -model-level doctests in the docstring for each model. + def add_two(num): + "Adds 2 to the given number and returns the result." + return num + 2 -For example:: + Because tests often make great documentation, putting tests directly in + your docstrings is an effective way to document *and* test your code. - from django.db import model +For a given Django application, the test runner looks for doctests in two +places: + + * The ``models.py`` file. You can define module-level doctests and/or a + doctest for individual models. It's common practice to put + application-level doctests in the module docstring and model-level + doctests in the model docstrings. + + * A file called ``tests.py`` in the application directory -- i.e., the + directory that holds ``models.py``. This file is a hook for any and all + doctests you want to write that aren't necessarily related to models. + +Here is an example model doctest:: + + # models.py + + from django.db import models class Animal(models.Model): """ @@ -78,38 +127,53 @@ For example:: >>> cat.speak() 'The cat says "meow"' """ - - name = models.CharField(maxlength=20) - sound = models.CharField(maxlength=20) + name = models.CharField(max_length=20) + sound = models.CharField(max_length=20) def speak(self): return 'The %s says "%s"' % (self.name, self.sound) -When you `run your tests`_, the test utility will find this docstring, notice +When you `run your tests`_, the test runner will find this docstring, notice that portions of it look like an interactive Python session, and execute those lines while checking that the results match. +In the case of model tests, note that the test runner takes care of creating +its own test database. That is, any test that accesses a database -- by +creating and saving model instances, for example -- will not affect your +production database. Each doctest begins with a "blank slate" -- a fresh +database containing an empty table for each model. (See the section on +fixtures, below, for more on this.) + For more details about how doctest works, see the `standard library documentation for doctest`_ .. _doctest: http://docs.python.org/lib/module-doctest.html .. _standard library documentation for doctest: doctest_ -Writing unittests ------------------ +Writing unit tests +------------------ Like doctests, Django's unit tests use a standard library module: unittest_. -As with doctests, Django's test runner looks for any unit test cases defined -in ``models.py``, or in a ``tests.py`` file stored in the application -directory. +This module uses a different way of defining tests, taking a class-based +approach. + +As with doctests, for a given Django application, the test runner looks for +unit tests in two places: + + * The ``models.py`` file. The test runner looks for any subclass of + ``unittest.TestCase`` in this module. -An equivalent unittest test case for the above example would look like:: + * A file called ``tests.py`` in the application directory -- i.e., the + directory that holds ``models.py``. Again, the test runner looks for any + subclass of ``unittest.TestCase`` in this module. + +This example ``unittest.TestCase`` subclass is equivalent to the example given +in the doctest section above:: import unittest from myapp.models import Animal class AnimalTestCase(unittest.TestCase): - def setUp(self): self.lion = Animal.objects.create(name="lion", sound="roar") self.cat = Animal.objects.create(name="cat", sound="meow") @@ -118,10 +182,17 @@ An equivalent unittest test case for the above example would look like:: self.assertEquals(self.lion.speak(), 'The lion says "roar"') self.assertEquals(self.cat.speak(), 'The cat says "meow"') -When you `run your tests`_, the test utility will find all the test cases -(that is, subclasses of ``unittest.TestCase``) in ``models.py`` and -``tests.py``, automatically build a test suite out of those test cases, -and run that suite. +When you `run your tests`_, the default behavior of the test utility is +to find all the test cases (that is, subclasses of ``unittest.TestCase``) +in ``models.py`` and ``tests.py``, automatically build a test suite out of +those test cases, and run that suite. + +In the Django development version, there is a second way to define the test +suite for a module: if you define a function called ``suite()`` in either +``models.py`` or ``tests.py``, the Django test runner will use that function +to construct the test suite for that module. This follows the +`suggested organization`_ for unit tests. See the Python documentation for +more details on how to construct a complex test suite. For more details about ``unittest``, see the `standard library unittest documentation`_. @@ -129,308 +200,557 @@ documentation`_. .. _unittest: http://docs.python.org/lib/module-unittest.html .. _standard library unittest documentation: unittest_ .. _run your tests: `Running tests`_ +.. _suggested organization: http://docs.python.org/lib/organizing-tests.html Which should I use? ------------------- -Choosing a test framework is often contentious, so Django simply supports -both of the standard Python test frameworks. Choosing one is up to each -developer's personal tastes; each is supported equally. Since each test -system has different benefits, the best approach is probably to use both -together, picking the test system to match the type of tests you need to -write. +Because Django supports both of the standard Python test frameworks, it's up to +you and your tastes to decide which one to use. You can even decide to use +*both*. -For developers new to testing, however, this choice can seem -confusing, so here are a few key differences to help you decide whether -doctests or unit tests are right for you. +For developers new to testing, however, this choice can seem confusing. Here, +then, are a few key differences to help you decide which approach is right for +you: -If you've been using Python for a while, ``doctest`` will probably feel more -"pythonic". It's designed to make writing tests as easy as possible, so -there's no overhead of writing classes or methods; you simply put tests in -docstrings. This gives the added advantage of giving your modules automatic -documentation -- well-written doctests can kill both the documentation and the -testing bird with a single stone. + * If you've been using Python for a while, ``doctest`` will probably feel + more "pythonic". It's designed to make writing tests as easy as possible, + so it requires no overhead of writing classes or methods. You simply put + tests in docstrings. This has the added advantage of serving as + documentation (and correct documentation, at that!). -For developers just getting started with testing, using doctests will probably -get you started faster. + If you're just getting started with testing, using doctests will probably + get you started faster. -The ``unittest`` framework will probably feel very familiar to developers -coming from Java. Since ``unittest`` is inspired by Java's JUnit, if -you've used testing frameworks in other languages that similarly were -inspired by JUnit, ``unittest`` should also feel pretty familiar. + * The ``unittest`` framework will probably feel very familiar to developers + coming from Java. ``unittest`` is inspired by Java's JUnit, so you'll + feel at home with this method if you've used JUnit or any test framework + inspired by JUnit. -Since ``unittest`` is organized around classes and methods, if you need -to write a bunch of tests that all share similar code, you can easily use -subclass to abstract common tasks; this makes test code shorter and cleaner. -There's also support for explicit setup and/or cleanup routines, which give -you a high level of control over the environment your test cases run in. + * If you need to write a bunch of tests that share similar code, then + you'll appreciate the ``unittest`` framework's organization around + classes and methods. This makes it easy to abstract common tasks into + common methods. The framework also supports explicit setup and/or cleanup + routines, which give you a high level of control over the environment + in which your test cases are run. Again, remember that you can use both systems side-by-side (even in the same -app). In the end, most projects will eventually end up using both; each shines +app). In the end, most projects will eventually end up using both. Each shines in different circumstances. -Testing Tools +Running tests ============= -To assist in testing various features of your application, Django provides -tools that can be used to establish tests and test conditions. +Once you've written tests, run them using your project's ``manage.py`` utility:: + + $ ./manage.py test + +By default, this will run every test in every application in ``INSTALLED_APPS``. +If you only want to run tests for a particular application, add the +application name to the command line. For example, if your ``INSTALLED_APPS`` +contains ``'myproject.polls'`` and ``'myproject.animals'``, you can run the +``myproject.animals`` unit tests alone with this command:: + + # ./manage.py test animals -* `Test Client`_ -* `TestCase`_ -* `E-mail services`_ +Note that we used ``animals``, not ``myproject.animals``. -Test Client ------------ +**New in Django development version:** If you use unit tests, as opposed to +doctests, you can be even *more* specific in choosing which tests to execute. +To run a single test case in an application (for example, the +``AnimalTestCase`` described in the "Writing unit tests" section), add the +name of the test case to the label on the command line:: -The Test Client is a simple dummy browser. It allows you to simulate -GET and POST requests on a URL, and observe the response that is received. -This allows you to test that the correct view is executed for a given URL, -and that the view constructs the correct response. + $ ./manage.py test animals.AnimalTestCase -As the response is generated, the Test Client gathers details on the -Template and Context objects that were used to generate the response. These -Templates and Contexts are then provided as part of the response, and can be -used as test conditions. +And it gets even more granular than that! To run a *single* test method inside +a test case, add the name of the test method to the label:: -.. admonition:: Test Client vs Browser Automation? + $ ./manage.py test animals.AnimalTestCase.testFluffyAnimals - The Test Client is not intended as a replacement for Twill_, Selenium_, - or other browser automation frameworks - it is intended to allow - testing of the contexts and templates produced by a view, - rather than the HTML rendered to the end-user. +Understanding the test output +----------------------------- - A comprehensive test suite should use a combination of both: Test Client - tests to establish that the correct view is being called and that - the view is collecting the correct context data, and Browser Automation - tests to check that user interface behaves as expected. +When you run your tests, you'll see a number of messages as the test runner +prepares itself:: + + Creating test database... + Creating table myapp_animal + Creating table myapp_mineral + Loading 'initial_data' fixtures... + No fixtures found. + +This tells you that the test runner is creating a test database -- a blank, +from-scratch database that it will use for any tests that happen to require a +database (namely, model tests). + +Don't worry -- the test runner will not touch your "real" (production) +database. It creates a separate database purely for the tests. This test +database gets its name by prepending ``test_`` to the value of the +``DATABASE_NAME`` setting. If you want to use a different name, specify the +``TEST_DATABASE_NAME`` setting. + +Aside from using a separate database, the test runner will otherwise use all of +the same database settings you have in your settings file: ``DATABASE_ENGINE``, +``DATABASE_USER``, ``DATABASE_HOST``, etc. The test database is created by the +user specified by ``DATABASE_USER``, so you'll need to make sure that the given +user account has sufficient privileges to create a new database on the system. + +**New in Django development version:** For fine-grained control over the +character encoding of your test database, use the ``TEST_DATABASE_CHARSET`` +setting. If you're using MySQL, you can also use the ``TEST_DATABASE_COLLATION`` +setting to control the particular collation used by the test database. See the +settings_ documentation for details of these advanced settings. + +.. _settings: ../settings/ + +Once the test database has been created, Django will run your tests. +If everything goes well, you'll see something like this:: + + ---------------------------------------------------------------------- + Ran 22 tests in 0.221s + + OK + +If there are test failures, however, you'll see full details about which tests +failed:: + + ====================================================================== + FAIL: Doctest: ellington.core.throttle.models + ---------------------------------------------------------------------- + Traceback (most recent call last): + File "/dev/django/test/doctest.py", line 2153, in runTest + raise self.failureException(self.format_failure(new.getvalue())) + AssertionError: Failed doctest test for myapp.models + File "/dev/myapp/models.py", line 0, in models + + ---------------------------------------------------------------------- + File "/dev/myapp/models.py", line 14, in myapp.models + Failed example: + throttle.check("actor A", "action one", limit=2, hours=1) + Expected: + True + Got: + False + + ---------------------------------------------------------------------- + Ran 2 tests in 0.048s + + FAILED (failures=1) + +A full explanation of this error output is beyond the scope of this document, +but it's pretty intuitive. You can consult the documentation of Python's +``unittest`` library for details. + +Note that the return code for the test-runner script is the total number of +failed and erroneous tests. If all the tests pass, the return code is 0. This +feature is useful if you're using the test-runner script in a shell script and +need to test for success or failure at that level. + +Regardless of whether the tests pass or fail, the test database is destroyed when +all the tests have been executed. + +Testing tools +============= + +Django provides a small set of tools that come in handy when writing tests. + +The test client +--------------- + +The test client is a Python class that acts as a dummy Web browser, allowing +you to test your views and interact with your Django-powered application +programatically. + +Some of the things you can do with the test client are: + + * Simulate GET and POST requests on a URL and observe the response -- + everything from low-level HTTP (result headers and status codes) to + page content. + + * Test that the correct view is executed for a given URL. + + * Test that a given request is rendered by a given Django template, with + a template context that contains certain values. + +Note that the test client is not intended to be a replacement for Twill_, +Selenium_, or other "in-browser" frameworks. Django's test client has +a different focus. In short: + + * Use Django's test client to establish that the correct view is being + called and that the view is collecting the correct context data. + + * Use in-browser frameworks such as Twill and Selenium to test *rendered* + HTML and the *behavior* of Web pages, namely JavaScript functionality. + +A comprehensive test suite should use a combination of both test types. .. _Twill: http://twill.idyll.org/ .. _Selenium: http://www.openqa.org/selenium/ +Overview and a quick example +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To use the test client, instantiate ``django.test.client.Client`` and retrieve +Web pages:: + + >>> from django.test.client import Client + >>> c = Client() + >>> response = c.post('/login/', {'username': 'john', 'password': 'smith'}) + >>> response.status_code + 200 + >>> response = c.get('/customer/details/') + >>> response.content + '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 ...' + +As this example suggests, you can instantiate ``Client`` from within a session +of the Python interactive interpreter. + +Note a few important things about how the test client works: + + * The test client does *not* require the Web server to be running. In fact, + it will run just fine with no Web server running at all! That's because + it avoids the overhead of HTTP and deals directly with the Django + framework. This helps make the unit tests run quickly. + + * When retrieving pages, remember to specify the *path* of the URL, not the + whole domain. For example, this is correct:: + + >>> c.get('/login/') + + This is incorrect:: + + >>> c.get('http://www.example.com/login/') + + The test client is not capable of retrieving Web pages that are not + powered by your Django project. If you need to retrieve other Web pages, + use a Python standard library module such as urllib_ or urllib2_. + + * To resolve URLs, the test client uses whatever URLconf is pointed-to by + your ``ROOT_URLCONF`` setting. + + * Although the above example would work in the Python interactive + interpreter, some of the test client's functionality, notably the + template-related functionality, is only available *while tests are running*. + + The reason for this is that Django's test runner performs a bit of black + magic in order to determine which template was loaded by a given view. + This black magic (essentially a patching of Django's template system in + memory) only happens during test running. + +.. _urllib: http://docs.python.org/lib/module-urllib.html +.. _urllib2: http://docs.python.org/lib/module-urllib2.html + Making requests ~~~~~~~~~~~~~~~ -Creating an instance of ``Client`` (``django.test.client.Client``) requires -no arguments at time of construction. Once constructed, the following methods -can be invoked on the ``Client`` instance. +Use the ``django.test.client.Client`` class to make requests. It requires no +arguments at time of construction:: + + >>> c = Client() + +Once you have a ``Client`` instance, you can call any of the following methods: ``get(path, data={})`` - Make a GET request on the provided ``path``. The key-value pairs in the - data dictionary will be used to create a GET data payload. For example:: + Makes a GET request on the provided ``path`` and returns a ``Response`` + object, which is documented below. + + The key-value pairs in the ``data`` dictionary are used to create a GET + data payload. For example:: - c = Client() - c.get('/customers/details/', {'name':'fred', 'age':7}) + >>> c = Client() + >>> c.get('/customers/details/', {'name': 'fred', 'age': 7}) - will result in the evaluation of a GET request equivalent to:: + ...will result in the evaluation of a GET request equivalent to:: - http://yoursite.com/customers/details/?name=fred&age=7 + /customers/details/?name=fred&age=7 ``post(path, data={}, content_type=MULTIPART_CONTENT)`` - Make a POST request on the provided ``path``. If you provide a content type - (e.g., ``text/xml`` for an XML payload), the contents of ``data`` will be - sent as-is in the POST request, using the content type in the HTTP - ``Content-Type`` header. + Makes a POST request on the provided ``path`` and returns a ``Response`` + object, which is documented below. - If you do not provide a value for ``content_type``, the values in + The key-value pairs in the ``data`` dictionary are used to submit POST + data. For example:: + + >>> c = Client() + >>> c.post('/login/', {'name': 'fred', 'passwd': 'secret'}) + + ...will result in the evaluation of a POST request to this URL:: + + /login/ + + ...with this POST data:: + + name=fred&passwd=secret + + If you provide ``content_type`` (e.g., ``text/xml`` for an XML payload), + the contents of ``data`` will be sent as-is in the POST request, using + ``content_type`` in the HTTP ``Content-Type`` header. + + If you don't provide a value for ``content_type``, the values in ``data`` will be transmitted with a content type of ``multipart/form-data``. - The key-value pairs in the data dictionary will be encoded as a multipart - message and used to create the POST data payload. + In this case, the key-value pairs in ``data`` will be encoded as a + multipart message and used to create the POST data payload. - To submit multiple values for a given key (for example, to specify - the selections for a multiple selection list), provide the values as a - list or tuple for the required key. For example, a data dictionary of - ``{'choices': ('a','b','d')}`` would submit three selected rows for the - field named ``choices``. + To submit multiple values for a given key -- for example, to specify + the selections for a ``<select multiple>`` -- provide the values as a + list or tuple for the required key. For example, this value of ``data`` + would submit three selected values for the field named ``choices``:: - Submitting files is a special case. To POST a file, you need only - provide the file field name as a key, and a file handle to the file you wish to - upload as a value. The Test Client will populate the two POST fields (i.e., - ``field`` and ``field_file``) required by Django's FileField. For example:: + {'choices': ('a', 'b', 'd')} - c = Client() - f = open('wishlist.doc') - c.post('/customers/wishes/', {'name':'fred', 'attachment':f}) - f.close() + Submitting files is a special case. To POST a file, you need only provide + the file field name as a key, and a file handle to the file you wish to + upload as a value. For example:: - will result in the evaluation of a POST request on ``/customers/wishes/``, - with a POST dictionary that contains ``name``, ``attachment`` (containing the - file name), and ``attachment_file`` (containing the file data). Note that you - need to manually close the file after it has been provided to the POST. + >>> c = Client() + >>> f = open('wishlist.doc') + >>> c.post('/customers/wishes/', {'name': 'fred', 'attachment': f}) + >>> f.close() + + (The name ``attachment`` here is not relevant; use whatever name your + file-processing code expects.) + + Note that you should manually close the file after it has been provided to + ``post()``. ``login(**credentials)`` **New in Django development version** - On a production site, it is likely that some views will be protected from - anonymous access through the use of the @login_required decorator, or some - other login checking mechanism. The ``login()`` method can be used to - simulate the effect of a user logging into the site. As a result of calling - this method, the Client will have all the cookies and session data required - to pass any login-based tests that may form part of a view. + If your site uses Django's `authentication system`_ and you deal with + logging in users, you can use the test client's ``login()`` method to + simulate the effect of a user logging into the site. + + After you call this method, the test client will have all the cookies and + session data required to pass any login-based tests that may form part of + a view. - In most cases, the ``credentials`` required by this method are the username - and password of the user that wants to log in, provided as keyword - arguments:: + The format of the ``credentials`` argument depends on which + `authentication backend`_ you're using (which is configured by your + ``AUTHENTICATION_BACKENDS`` setting). If you're using the standard + authentication backend provided by Django (``ModelBackend``), + ``credentials`` should be the user's username and password, provided as + keyword arguments:: - c = Client() - c.login(username='fred', password='secret') - # Now you can access a login protected view + >>> c = Client() + >>> c.login(username='fred', password='secret') + >>> # Now you can access a view that's only available to logged-in users. - If you are using a different authentication backend, this method may - require different credentials. + If you're using a different authentication backend, this method may require + different credentials. It requires whichever credentials are required by + your backend's ``authenticate()`` method. ``login()`` returns ``True`` if it the credentials were accepted and login was successful. - Note that since the test suite will be executed using the test database, - which contains no users by default. As a result, logins that are valid - on your production site will not work under test conditions. You will - need to create users as part of the test suite (either manually, or - using a test fixture). + Finally, you'll need to remember to create user accounts before you can use + this method. As we explained above, the test runner is executed using a + test database, which contains no users by default. As a result, user + accounts that are valid on your production site will not work under test + conditions. You'll need to create users as part of the test suite -- either + manually (using the Django model API) or with a test fixture. + +``logout()`` + **New in Django development version** + + If your site uses Django's `authentication system`_, the ``logout()`` + method can be used to simulate the effect of a user logging out of + your site. -Testing Responses + After you call this method, the test client will have all the cookies and + session data cleared to defaults. Subsequent requests will appear to + come from an AnonymousUser. + +.. _authentication system: ../authentication/ +.. _authentication backend: ../authentication/#other-authentication-sources + +Testing responses ~~~~~~~~~~~~~~~~~ -The ``get()`` and ``post()`` methods both return a Response object. This -Response object has the following properties that can be used for testing -purposes: +The ``get()`` and ``post()`` methods both return a ``Response`` object. This +``Response`` object is *not* the same as the ``HttpResponse`` object returned +Django views; this object is simpler and has some additional data useful for +tests. + +Specifically, a ``Response`` object has the following attributes: =============== ========================================================== - Property Description + Attribute Description =============== ========================================================== - ``status_code`` The HTTP status of the response. See RFC2616_ for a - full list of HTTP status codes. + ``status_code`` The HTTP status of the response, as an integer. See + RFC2616_ for a full list of HTTP status codes. - ``content`` The body of the response. This is the final page - content as rendered by the view, or any error message - (such as the URL for a 302 redirect). + ``content`` The body of the response, as a string. This is the final + page content as rendered by the view, or any error + message (such as the URL for a 302 redirect). - ``template`` The Template instance that was used to render the final - content. Testing ``template.name`` can be particularly - useful; if the template was loaded from a file, - ``template.name`` will be the file name that was loaded. + ``template`` The ``Template`` instance that was used to render the + final content. Use ``template.name`` to get the + template's file name, if the template was loaded from a + file. (The name is a string such as + ``'admin/index.html'``.) - If multiple templates were rendered, (e.g., if one - template includes another template),``template`` will - be a list of Template objects, in the order in which - they were rendered. + If the rendered page used multiple templates -- e.g., + using `template inheritance`_ -- then ``template`` will + be a list of ``Template`` instances, in the order in + which they were rendered. - ``context`` The Context that was used to render the template that - produced the response content. + ``context`` The template ``Context`` instance that was used to render + the template that produced the response content. - As with ``template``, if multiple templates were rendered - ``context`` will be a list of Context objects, stored in - the order in which they were rendered. + As with ``template``, if the rendered page used multiple + templates, then ``context`` will be a list of ``Context`` + objects, in the order in which they were rendered. =============== ========================================================== .. _RFC2616: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html +.. _template inheritance: ../templates/#template-inheritance Exceptions ~~~~~~~~~~ -If you point the Test Client at a view that raises an exception, that exception +If you point the test client at a view that raises an exception, that exception will be visible in the test case. You can then use a standard ``try...catch`` -block, or ``unittest.TestCase.assertRaises()`` to test for exceptions. +block or ``unittest.TestCase.assertRaises()`` to test for exceptions. -The only exceptions that are not visible in a Test Case are ``Http404``, +The only exceptions that are not visible to the test client are ``Http404``, ``PermissionDenied`` and ``SystemExit``. Django catches these exceptions -internally and converts them into the appropriate HTTP responses codes. +internally and converts them into the appropriate HTTP response codes. In these +cases, you can check ``response.status_code`` in your test. Persistent state ~~~~~~~~~~~~~~~~ -The Test Client is stateful; if a cookie is returned as part of a response, -that cookie is provided as part of the next request issued by that Client -instance. Expiry policies for these cookies are not followed; if you want -a cookie to expire, either delete it manually or create a new Client -instance (which will effectively delete all cookies). +The test client is stateful. If a response returns a cookie, then that cookie +will be stored in the test client and sent with all subsequent ``get()`` and +``post()`` requests. + +Expiration policies for these cookies are not followed. If you want a cookie +to expire, either delete it manually or create a new ``Client`` instance (which +will effectively delete all cookies). -There are two properties of the Test Client which are used to store persistent -state information. If necessary, these properties can be interrogated as -part of a test condition. +A test client has two attributes that store persistent state information. You +can access these properties as part of a test condition. =============== ========================================================== - Property Description + Attribute Description =============== ========================================================== ``cookies`` A Python ``SimpleCookie`` object, containing the current - values of all the client cookies. + values of all the client cookies. See the + `Cookie module documentation`_ for more. ``session`` A dictionary-like object containing session information. See the `session documentation`_ for full details. =============== ========================================================== -.. _`session documentation`: ../sessions/ +.. _Cookie module documentation: http://docs.python.org/lib/module-Cookie.html +.. _session documentation: ../sessions/ Example ~~~~~~~ -The following is a simple unit test using the Test Client:: +The following is a simple unit test using the test client:: import unittest from django.test.client import Client class SimpleTest(unittest.TestCase): def setUp(self): - # Every test needs a client + # Every test needs a client. self.client = Client() + def test_details(self): - # Issue a GET request + # Issue a GET request. response = self.client.get('/customer/details/') - # Check that the respose is 200 OK + # Check that the respose is 200 OK. self.failUnlessEqual(response.status_code, 200) - # Check that the rendered context contains 5 customers + + # Check that the rendered context contains 5 customers. self.failUnlessEqual(len(response.context['customers']), 5) TestCase -------- -Normal python unit tests extend a base class of ``unittest.testCase``. -Django provides an extension of this base class - ``django.test.TestCase`` -- that provides some additional capabilities that can be useful for -testing web sites. +Normal Python unit test classes extend a base class of ``unittest.TestCase``. +Django provides an extension of this base class -- ``django.test.TestCase`` +-- that provides some additional capabilities that can be useful for +testing Web sites. -Moving from a normal unittest TestCase to a Django TestCase is easy - just -change the base class of your test from ``unittest.TestCase`` to -``django.test.TestCase``. All of the standard Python unit test facilities -will continue to be available, but they will be augmented with some useful -extra facilities. +Converting a normal ``unittest.TestCase`` to a Django ``TestCase`` is easy: +just change the base class of your test from ``unittest.TestCase`` to +``django.test.TestCase``. All of the standard Python unit test functionality +will continue to be available, but it will be augmented with some useful +additions. -Default Test Client +Default test client ~~~~~~~~~~~~~~~~~~~ + **New in Django development version** Every test case in a ``django.test.TestCase`` instance has access to an -instance of a Django `Test Client`_. This Client can be accessed as -``self.client``. This client is recreated for each test. +instance of a Django test client. This client can be accessed as +``self.client``. This client is recreated for each test, so you don't have to +worry about state (such as cookies) carrying over from one test to another. + +This means, instead of instantiating a ``Client`` in each test:: + + import unittest + from django.test.client import Client + + class SimpleTest(unittest.TestCase): + def test_details(self): + client = Client() + response = client.get('/customer/details/') + self.failUnlessEqual(response.status_code, 200) + + def test_index(self): + client = Client() + response = client.get('/customer/index/') + self.failUnlessEqual(response.status_code, 200) + +...you can just refer to ``self.client``, like so:: + + from django.test import TestCase + from django.test.client import Client + + class SimpleTest(TestCase): + def test_details(self): + response = self.client.get('/customer/details/') + self.failUnlessEqual(response.status_code, 200) + + def test_index(self): + response = self.client.get('/customer/index/') + self.failUnlessEqual(response.status_code, 200) Fixture loading ~~~~~~~~~~~~~~~ -A test case for a database-backed website isn't much use if there isn't any +A test case for a database-backed Web site isn't much use if there isn't any data in the database. To make it easy to put test data into the database, -Django provides a fixtures framework. +Django's custom ``TestCase`` class provides a way of loading **fixtures**. + +A fixture is a collection of data that Django knows how to import into a +database. For example, if your site has user accounts, you might set up a +fixture of fake user accounts in order to populate your database during tests. -A *Fixture* is a collection of files that contain the serialized contents of -the database. Each fixture has a unique name; however, the files that -comprise the fixture can be distributed over multiple directories, in -multiple applications. +The most straightforward way of creating a fixture is to use the +``manage.py dumpdata`` command. This assumes you already have some data in +your database. See the `dumpdata documentation`_ for more details. .. note:: - If you have synchronized a Django project, you have already experienced - the use of one fixture -- the ``initial_data`` fixture. Every time you - synchronize the database, Django installs the ``initial_data`` fixture. - This provides a mechanism to populate a new database with any initial - data (such as a default set of categories). Fixtures with other names - can be installed manually using ``django-admin.py loaddata``. + If you've ever run ``manage.py syncdb``, you've already used a fixture + without even knowing it! When you call ``syncdb`` in the database for + the first time, Django installs a fixture called ``initial_data``. + This gives you a way of populating a new database with any initial data, + such as a default set of categories. -However, for the purposes of unit testing, each test must be able to -guarantee the contents of the database at the start of each and every -test. + Fixtures with other names can always be installed manually using the + ``manage.py loaddata`` command. -To define a fixture for a test, all you need to do is add a class -attribute to your test describing the fixtures you want the test to use. -For example, the test case from `Writing unittests`_ would -look like:: +Once you've created a fixture and placed it somewhere in your Django project, +you can use it in your unit tests by specifying a ``fixtures`` class attribute +on your ``django.test.TestCase`` subclass:: from django.test import TestCase from myapp.models import Animal @@ -439,229 +759,209 @@ look like:: fixtures = ['mammals.json', 'birds'] def setUp(self): - # test definitions as before + # Test definitions as before. + + def testFluffyAnimals(self): + # A test that uses the fixtures. + +Here's specifically what will happen: -At the start of each test case, before ``setUp()`` is run, Django will -flush the database, returning the database the state it was in directly -after ``syncdb`` was called. Then, all the named fixtures are installed. -In this example, any JSON fixture called ``mammals``, and any fixture -named ``birds`` will be installed. See the documentation on -`loading fixtures`_ for more details on defining and installing fixtures. + * At the start of each test case, before ``setUp()`` is run, Django will + flush the database, returning the database to the state it was in + directly after ``syncdb`` was called. -.. _`loading fixtures`: ../django-admin/#loaddata-fixture-fixture + * Then, all the named fixtures are installed. In this example, Django will + install any JSON fixture named ``mammals``, followed by any fixture named + ``birds``. See the `loaddata documentation`_ for more details on defining + and installing fixtures. This flush/load procedure is repeated for each test in the test case, so you can be certain that the outcome of a test will not be affected by -another test, or the order of test execution. +another test, or by the order of test execution. + +.. _dumpdata documentation: ../django-admin/#dumpdata-appname-appname +.. _loaddata documentation: ../django-admin/#loaddata-fixture-fixture Emptying the test outbox ~~~~~~~~~~~~~~~~~~~~~~~~ + **New in Django development version** -At the start of each test case, in addition to installing fixtures, -Django clears the contents of the test e-mail outbox. +If you use Django's custom ``TestCase`` class, the test runner will clear the +contents of the test e-mail outbox at the start of each test case. For more detail on e-mail services during tests, see `E-mail services`_. Assertions ~~~~~~~~~~ + **New in Django development version** -Normal Python unit tests have a wide range of assertions, such as -``assertTrue`` and ``assertEquals`` that can be used to validate behavior. -``django.TestCase`` adds to these, providing some assertions -that can be useful in testing the behavior of web sites. +As Python's normal ``unittest.TestCase`` class implements assertion +methods such as ``assertTrue`` and ``assertEquals``, Django's custom +``TestCase`` class provides a number of custom assertion methods that are +useful for testing Web applications: -``assertContains(response, text, count=1, status_code=200)`` - Assert that a response indicates that a page could be retrieved and - produced the nominated status code, and that ``text`` occurs ``count`` - times in the content of the response. +``assertContains(response, text, count=None, status_code=200)`` + Asserts that a ``Response`` instance produced the given ``status_code`` and + that ``text`` appears in the content of the response. If ``count`` is + provided, ``text`` must occur exactly ``count`` times in the response. ``assertFormError(response, form, field, errors)`` - Assert that a field on a form raised the provided list of errors when + Asserts that a field on a form raises the provided list of errors when rendered on the form. - ``form`` is the name the form object was given in the template context. + ``form`` is the name the ``Form`` instance was given in the template + context. Note that this works only for ``newforms.Form`` instances, not + ``oldforms.Form`` instances. ``field`` is the name of the field on the form to check. If ``field`` - has a value of ``None``, non-field errors will be checked. + has a value of ``None``, non-field errors (errors you can access via + ``form.non_field_errors()``) will be checked. ``errors`` is an error string, or a list of error strings, that are expected as a result of form validation. ``assertTemplateNotUsed(response, template_name)`` - Assert that the template with the given name was *not* used in rendering + Asserts that the template with the given name was *not* used in rendering the response. ``assertRedirects(response, expected_path, status_code=302, target_status_code=200)`` - Assert that the response received produced the nominated status code, - redirects the browser to the provided path, and that retrieving the provided - path yields a response with the target status code. + Asserts that the response return a ``status_code`` redirect status, + it redirected to ``expected_path`` and the subsequent page was received with + ``target_status_code``. ``assertTemplateUsed(response, template_name)`` - Assert that the template with the given name was used in rendering the + Asserts that the template with the given name was used in rendering the response. + The name is a string such as ``'admin/index.html'``. + E-mail services --------------- **New in Django development version** -If your view makes use of the `Django e-mail services`_, you don't really -want e-mail to be sent every time you run a test using that view. +If any of your Django views send e-mail using `Django's e-mail functionality`_, +you probably don't want to send e-mail each time you run a test using that +view. For this reason, Django's test runner automatically redirects all +Django-sent e-mail to a dummy outbox. This lets you test every aspect of +sending e-mail -- from the number of messages sent to the contents of each +message -- without actually sending the messages. + +The test runner accomplishes this by transparently replacing the normal +`SMTPConnection`_ class with a different version. (Don't worry -- this has no +effect on any other e-mail senders outside of Django, such as your machine's +mail server, if you're running one.) + +During test running, each outgoing e-mail is saved in +``django.core.mail.outbox``. This is a simple list of all `EmailMessage`_ +instances that have been sent. It does not exist under normal execution +conditions, i.e., when you're not running unit tests. The outbox is created +during test setup, along with the dummy `SMTPConnection`_. When the test +framework is torn down, the standard `SMTPConnection`_ class is restored, and +the test outbox is destroyed. -When the Django test framework is initialized, it transparently replaces the -normal `SMTPConnection`_ class with a dummy implementation that redirects all -e-mail to a dummy outbox. This outbox, stored as ``django.core.mail.outbox``, -is a simple list of all `EmailMessage`_ instances that have been sent. -For example, during test conditions, it would be possible to run the following -code:: +Here's an example test that examines ``django.core.mail.outbox`` for length +and contents:: from django.core import mail + from django.test import TestCase - # Send message - mail.send_mail('Subject here', 'Here is the message.', 'from@example.com', - ['to@example.com'], fail_silently=False) + class EmailTest(TestCase): + def test_send_email(self): + # Send message. + mail.send_mail('Subject here', 'Here is the message.', + 'from@example.com', ['to@example.com'], + fail_silently=False) - # One message has been sent - self.assertEqual(len(mail.outbox), 1) - # Subject of first message is correct - self.assertEqual(mail.outbox[0].subject, 'Subject here') + # Test that one message has been sent. + self.assertEqual(len(mail.outbox), 1) -The ``mail.outbox`` object does not exist under normal execution conditions. -The outbox is created during test setup, along with the dummy `SMTPConnection`_. -When the test framework is torn down, the standard `SMTPConnection`_ class -is restored, and the test outbox is destroyed. + # Verify that the subject of the first message is correct. + self.assertEqual(mail.outbox[0].subject, 'Subject here') As noted `previously`_, the test outbox is emptied at the start of every -test in a Django TestCase. To empty the outbox manually, assign the empty list -to mail.outbox:: +test in a Django ``TestCase``. To empty the outbox manually, assign the +empty list to ``mail.outbox``:: from django.core import mail # Empty the test outbox mail.outbox = [] -.. _`Django e-mail services`: ../email/ +.. _`Django's e-mail functionality`: ../email/ .. _`SMTPConnection`: ../email/#the-emailmessage-and-smtpconnection-classes .. _`EmailMessage`: ../email/#the-emailmessage-and-smtpconnection-classes .. _`previously`: #emptying-the-test-outbox -Running tests -============= +Using different testing frameworks +================================== -Run your tests using your project's ``manage.py`` utility:: +Clearly, ``doctest`` and ``unittest`` are not the only Python testing +frameworks. While Django doesn't provide explicit support for alternative +frameworks, it does provide a way to invoke tests constructed for an +alternative framework as if they were normal Django tests. - $ ./manage.py test - -If you only want to run tests for a particular application, add the -application name to the command line. For example, if your -``INSTALLED_APPS`` contains ``myproject.polls`` and ``myproject.animals``, -but you only want to run the animals unit tests, run:: - - $ ./manage.py test animals - -When you run your tests, you'll see a bunch of text flow by as the test -database is created and models are initialized. This test database is -created from scratch every time you run your tests. - -By default, the test database gets its name by prepending ``test_`` to -the database name specified by the ``DATABASE_NAME`` setting; all other -database settings will the same as they would be for the project normally. -If you wish to use a name other than the default for the test database, -you can use the ``TEST_DATABASE_NAME`` setting to provide a name. +When you run ``./manage.py test``, Django looks at the ``TEST_RUNNER`` +setting to determine what to do. By default, ``TEST_RUNNER`` points to +``'django.test.simple.run_tests'``. This method defines the default Django +testing behavior. This behavior involves: -**New in Django development version:** For fine-grained control over the -character encoding of your database, use the ``TEST_DATABASE_CHARSET`` setting. -If you're using MySQL, you can also use the ``TEST_DATABASE_COLLATION`` setting -to control the particular collation used by the test database. See the -settings_ documentation for details of these advanced settings. + #. Performing global pre-test setup. -.. _settings: ../settings/ + #. Creating the test database. -The test database is created by the user in the ``DATABASE_USER`` setting. -This user needs to have sufficient privileges to create a new database on the -system. + #. Running ``syncdb`` to install models and initial data into the test + database. -Once the test database has been established, Django will run your tests. -If everything goes well, at the end you'll see:: + #. Looking for unit tests and doctests in the ``models.py`` and + ``tests.py`` files in each installed application. - ---------------------------------------------------------------------- - Ran 22 tests in 0.221s + #. Running the unit tests and doctests that are found. - OK + #. Destroying the test database. -If there are test failures, however, you'll see full details about what tests -failed:: + #. Performing global post-test teardown. - ====================================================================== - FAIL: Doctest: ellington.core.throttle.models - ---------------------------------------------------------------------- - Traceback (most recent call last): - File "/dev/django/test/doctest.py", line 2153, in runTest - raise self.failureException(self.format_failure(new.getvalue())) - AssertionError: Failed doctest test for myapp.models - File "/dev/myapp/models.py", line 0, in models +If you define your own test runner method and point ``TEST_RUNNER`` at that +method, Django will execute your test runner whenever you run +``./manage.py test``. In this way, it is possible to use any test framework +that can be executed from Python code. - ---------------------------------------------------------------------- - File "/dev/myapp/models.py", line 14, in myapp.models - Failed example: - throttle.check("actor A", "action one", limit=2, hours=1) - Expected: - True - Got: - False - - ---------------------------------------------------------------------- - Ran 2 tests in 0.048s - - FAILED (failures=1) - -The return code for the script is the total number of failed and erroneous -tests. If all the tests pass, the return code is 0. +Defining a test runner +---------------------- -Regardless of whether the tests pass or fail, the test database is destroyed when -all the tests have been executed. +**New in Django development version** -Using a different testing framework -=================================== +By convention, a test runner should be called ``run_tests``. The only strict +requirement is that it has the same arguments as the Django test runner: -Doctest and Unittest are not the only Python testing frameworks. While -Django doesn't provide explicit support these alternative frameworks, -it does provide a mechanism to allow you to invoke tests constructed for -an alternative framework as if they were normal Django tests. +``run_tests(test_labels, verbosity=1, interactive=True, extra_tests=[])`` -When you run ``./manage.py test``, Django looks at the ``TEST_RUNNER`` -setting to determine what to do. By default, ``TEST_RUNNER`` points to -``django.test.simple.run_tests``. This method defines the default Django -testing behavior. This behavior involves: + ``test_labels`` is a list of strings describing the tests to be run. A test + label can take one of three forms: -#. Performing global pre-test setup -#. Creating the test database -#. Running ``syncdb`` to install models and initial data into the test database -#. Looking for Unit Tests and Doctests in ``models.py`` and ``tests.py`` file for each installed application -#. Running the Unit Tests and Doctests that are found -#. Destroying the test database -#. Performing global post-test teardown + * ``app.TestCase.test_method`` -- Run a single test method in a test case. + * ``app.TestCase`` -- Run all the test methods in a test case. + * ``app`` -- Search for and run all tests in the named application. -If you define your own test runner method and point ``TEST_RUNNER`` -at that method, Django will execute your test runner whenever you run -``./manage.py test``. In this way, it is possible to use any test -framework that can be executed from Python code. + If ``test_labels`` has a value of ``None``, the test runner should run + search for tests in all the applications in ``INSTALLED_APPS``. -Defining a test runner ----------------------- -By convention, a test runner should be called ``run_tests``; however, you -can call it anything you want. The only requirement is that it accept two -arguments: + ``verbosity`` determines the amount of notification and debug information + that will be printed to the console; ``0`` is no output, ``1`` is normal + output, and ``2`` is verbose output. -``run_tests(module_list, verbosity=1)`` - The module list is the list of Python modules that contain the models to be - tested. This is the same format returned by ``django.db.models.get_apps()`` + If ``interactive`` is ``True``, the test suite has permission to ask the + user for instructions when the test suite is executed. An example of this + behavior would be asking for permission to delete an existing test + database. If ``interactive`` is ``False``, the test suite must be able to + run without any manual intervention. - Verbosity determines the amount of notification and debug information that - will be printed to the console; ``0`` is no output, ``1`` is normal output, - and ``2`` is verbose output. + ``extra_tests`` is a list of extra ``TestCase`` instances to add to the + suite that is executed by the test runner. These extra tests are run + in addition to those discovered in the modules listed in ``module_list``. This method should return the number of tests that failed. @@ -674,28 +974,36 @@ a number of utility methods in the ``django.test.utils`` module. ``setup_test_environment()`` Performs any global pre-test setup, such as the installing the instrumentation of the template rendering system and setting up - the dummy SMTPConnection. + the dummy ``SMTPConnection``. ``teardown_test_environment()`` - Performs any global post-test teardown, such as removing the instrumentation - of the template rendering system and restoring normal e-mail services. + Performs any global post-test teardown, such as removing the + black magic hooks into the template system and restoring normal e-mail + services. ``create_test_db(verbosity=1, autoclobber=False)`` - Creates a new test database, and run ``syncdb`` against it. + Creates a new test database and runs ``syncdb`` against it. - ``verbosity`` has the same behavior as in the test runner. + ``verbosity`` has the same behavior as in ``run_tests()``. - ``Autoclobber`` describes the behavior that will occur if a database with - the same name as the test database is discovered. If ``autoclobber`` is False, - the user will be asked to approve destroying the existing database. ``sys.exit`` - is called if the user does not approve. If autoclobber is ``True``, the database - will be destroyed without consulting the user. + ``autoclobber`` describes the behavior that will occur if a database with + the same name as the test database is discovered: + + * If ``autoclobber`` is ``False``, the user will be asked to approve + destroying the existing database. ``sys.exit`` is called if the user + does not approve. + + * If autoclobber is ``True``, the database will be destroyed without + consulting the user. ``create_test_db()`` has the side effect of modifying ``settings.DATABASE_NAME`` to match the name of the test database. + New in the Django development version, this function returns the name of + the test database that it created. + ``destroy_test_db(old_database_name, verbosity=1)`` - Destroys the database with the name ``settings.DATABASE_NAME`` matching, - and restores the value of ``settings.DATABASE_NAME`` to the provided name. + Destroys the database whose name is in the ``DATABASE_NAME`` setting + and restores the value of ``DATABASE_NAME`` to the provided name. - ``verbosity`` has the same behavior as in the test runner. + ``verbosity`` has the same behavior as in ``run_tests()``. diff --git a/docs/tutorial01.txt b/docs/tutorial01.txt index fdac9c554e..cf2b76e9be 100644 --- a/docs/tutorial01.txt +++ b/docs/tutorial01.txt @@ -22,10 +22,10 @@ installed. .. admonition:: Where to get help: If you're having trouble going through this tutorial, please post a message - to `django-users`_ or drop by `#django`_ on ``irc.freenode.net`` and we'll + to `django-users`_ or drop by `#django`_ on ``irc.freenode.net`` and we'll try to help. -.. _django-users: http://groups.google.com/group/django-users +.. _django-users: http://groups.google.com/group/django-users .. _#django: irc://irc.freenode.net/django Creating a project @@ -42,7 +42,7 @@ code, then run the command ``django-admin.py startproject mysite``. This will create a ``mysite`` directory in your current directory. .. note:: - + You'll need to avoid naming projects after built-in Python or Django components. In particular, this means you should avoid using names like ``django`` (which will conflict with Django itself) or ``site`` (which @@ -251,12 +251,12 @@ These concepts are represented by simple Python classes. Edit the from django.db import models class Poll(models.Model): - question = models.CharField(maxlength=200) + question = models.CharField(max_length=200) pub_date = models.DateTimeField('date published') class Choice(models.Model): poll = models.ForeignKey(Poll) - choice = models.CharField(maxlength=200) + choice = models.CharField(max_length=200) votes = models.IntegerField() The code is straightforward. Each model is represented by a class that @@ -279,7 +279,7 @@ name for ``Poll.pub_date``. For all other fields in this model, the field's machine-readable name will suffice as its human-readable name. Some ``Field`` classes have required elements. ``CharField``, for example, -requires that you give it a ``maxlength``. That's used not only in the database +requires that you give it a ``max_length``. That's used not only in the database schema, but in validation, as we'll soon see. Finally, note a relationship is defined, using ``models.ForeignKey``. That tells @@ -321,7 +321,7 @@ Now Django knows ``mysite`` includes the ``polls`` app. Let's run another comman python manage.py sql polls -You should see something similar to the following (the CREATE TABLE SQL statements +You should see something similar to the following (the CREATE TABLE SQL statements for the polls app):: BEGIN; @@ -341,7 +341,7 @@ for the polls app):: Note the following: * The exact output will vary depending on the database you are using. - + * Table names are automatically generated by combining the name of the app (``polls``) and the lowercase name of the model -- ``poll`` and ``choice``. (You can override this behavior.) @@ -371,8 +371,8 @@ If you're interested, also run the following commands: construction of your models. * ``python manage.py sqlcustom polls`` -- Outputs any custom SQL statements - (such as table modifications or constraints) that are defined for the - application. + (such as table modifications or constraints) that are defined for the + application. * ``python manage.py sqlclear polls`` -- Outputs the necessary ``DROP TABLE`` statements for this app, according to which tables already exist @@ -444,8 +444,8 @@ Once you're in the shell, explore the database API:: [] # Create a new Poll. - >>> from datetime import datetime - >>> p = Poll(question="What's up?", pub_date=datetime.now()) + >>> import datetime + >>> p = Poll(question="What's up?", pub_date=datetime.datetime.now()) # Save the object into the database. You have to call save() explicitly. >>> p.save() @@ -461,10 +461,10 @@ Once you're in the shell, explore the database API:: >>> p.question "What's up?" >>> p.pub_date - datetime.datetime(2005, 7, 15, 12, 00, 53) + datetime.datetime(2007, 7, 15, 12, 00, 53) # Change values by changing the attributes, then calling save(). - >>> p.pub_date = datetime(2005, 4, 1, 0, 0) + >>> p.pub_date = datetime.datetime(2007, 4, 1, 0, 0) >>> p.save() # objects.all() displays all the polls in the database. @@ -474,22 +474,39 @@ Once you're in the shell, explore the database API:: Wait a minute. ``<Poll: Poll object>`` is, utterly, an unhelpful representation of this object. Let's fix that by editing the polls model (in -the ``polls/models.py`` file) and adding a ``__str__()`` method to both +the ``polls/models.py`` file) and adding a ``__unicode__()`` method to both ``Poll`` and ``Choice``:: class Poll(models.Model): # ... - def __str__(self): + def __unicode__(self): return self.question class Choice(models.Model): # ... - def __str__(self): + def __unicode__(self): return self.choice -It's important to add ``__str__()`` methods to your models, not only for your -own sanity when dealing with the interactive prompt, but also because objects' -representations are used throughout Django's automatically-generated admin. +It's important to add ``__unicode__()`` methods to your models, not only for +your own sanity when dealing with the interactive prompt, but also because +objects' representations are used throughout Django's automatically-generated +admin. + +.. admonition:: Why ``__unicode__()`` and not ``__str__()``? + + If you're familiar with Python, you might be in the habit of adding + ``__str__()`` methods to your classes, not ``__unicode__()`` methods. + We use ``__unicode__()`` here because Django models deal with Unicode by + default. All data stored in your database is converted to Unicode when it's + returned. + + Django models have a default ``__str__()`` method that calls + ``__unicode__()`` and converts the result to a UTF-8 bytestring. This means + that ``unicode(p)`` will return a Unicode string, and ``str(p)`` will return + a normal string, with characters encoded as UTF-8. + + If all of this is jibberish to you, just remember to add ``__unicode__()`` + methods to your models. With any luck, things should Just Work for you. Note these are normal Python methods. Let's add a custom method, just for demonstration:: @@ -509,7 +526,7 @@ Let's jump back into the Python interactive shell by running >>> from mysite.polls.models import Poll, Choice - # Make sure our __str__() addition worked. + # Make sure our __unicode__() addition worked. >>> Poll.objects.all() [<Poll: What's up?>] @@ -520,9 +537,9 @@ Let's jump back into the Python interactive shell by running >>> Poll.objects.filter(question__startswith='What') [<Poll: What's up?>] - # Get the poll whose year is 2005. Of course, if you're going through this + # Get the poll whose year is 2007. Of course, if you're going through this # tutorial in another year, change as appropriate. - >>> Poll.objects.get(pub_date__year=2005) + >>> Poll.objects.get(pub_date__year=2007) <Poll: What's up?> >>> Poll.objects.get(id=2) @@ -563,9 +580,9 @@ Let's jump back into the Python interactive shell by running # The API automatically follows relationships as far as you need. # Use double underscores to separate relationships. - # This works as many levels deep as you want. There's no limit. - # Find all Choices for any poll whose pub_date is in 2005. - >>> Choice.objects.filter(poll__pub_date__year=2005) + # This works as many levels deep as you want; there's no limit. + # Find all Choices for any poll whose pub_date is in 2007. + >>> Choice.objects.filter(poll__pub_date__year=2007) [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>] # Let's delete one of the choices. Use delete() for that. diff --git a/docs/tutorial02.txt b/docs/tutorial02.txt index 99f586b4a1..d96c38f2bd 100644 --- a/docs/tutorial02.txt +++ b/docs/tutorial02.txt @@ -240,7 +240,7 @@ default, provide enough fields for 3 Choices." Then change the other fields in ``Choice`` to give them ``core=True``:: - choice = models.CharField(maxlength=200, core=True) + choice = models.CharField(max_length=200, core=True) votes = models.IntegerField(core=True) This tells Django: "When you edit a Choice on the Poll admin page, the 'choice' @@ -362,8 +362,8 @@ think they should. Customize the admin look and feel ================================= -Clearly, having "Django administration" and "example.com" at the top of each -admin page is ridiculous. It's just placeholder text. +Clearly, having "Django administration" at the top of each admin page is +ridiculous. It's just placeholder text. That's easy to change, though, using Django's template system. The Django admin is powered by Django itself, and its interfaces use Django's own template @@ -389,7 +389,7 @@ as above, then copy ``django/contrib/admin/templates/admin/base_site.html`` to ``admin`` subdirectory. Then, just edit the file and replace the generic Django text with your own -site's name and URL as you see fit. +site's name as you see fit. Note that any of Django's default admin templates can be overridden. To override a template, just do the same thing you did with ``base_site.html`` -- diff --git a/docs/tutorial03.txt b/docs/tutorial03.txt index 41febe021d..d49a417dcf 100644 --- a/docs/tutorial03.txt +++ b/docs/tutorial03.txt @@ -355,8 +355,9 @@ view code. Use the template system ======================= -Back to our ``polls.detail`` view. Given the context variable ``poll``, here's -what the template might look like:: +Back to the ``detail()`` view for our poll application. Given the context +variable ``poll``, here's what the "polls/detail.html" template might look +like:: <h1>{{ poll.question }}</h1> <ul> diff --git a/docs/tutorial04.txt b/docs/tutorial04.txt index 5cc12c445d..bd16fa2924 100644 --- a/docs/tutorial04.txt +++ b/docs/tutorial04.txt @@ -8,8 +8,8 @@ application and will focus on simple form processing and cutting down our code. Write a simple form =================== -Let's update our poll detail template from the last tutorial, so that the -template contains an HTML ``<form>`` element:: +Let's update our poll detail template ("polls/detail.html") from the last +tutorial, so that the template contains an HTML ``<form>`` element:: <h1>{{ poll.question }}</h1> @@ -193,7 +193,7 @@ Change it like so:: urlpatterns = patterns('', (r'^$', 'django.views.generic.list_detail.object_list', info_dict), (r'^(?P<object_id>\d+)/$', 'django.views.generic.list_detail.object_detail', info_dict), - (r'^(?P<object_id>\d+)/results/$', 'django.views.generic.list_detail.object_detail', dict(info_dict, template_name='polls/results.html')), + url(r'^(?P<object_id>\d+)/results/$', 'django.views.generic.list_detail.object_detail', dict(info_dict, template_name='polls/results.html'), 'poll_results'), (r'^(?P<poll_id>\d+)/vote/$', 'mysite.polls.views.vote'), ) @@ -209,6 +209,15 @@ objects" and "display a detail page for a particular type of object." from the URL to be called ``"object_id"``, so we've changed ``poll_id`` to ``object_id`` for the generic views. + * We've added a name, ``poll_results``, to the results view so that we + have a way to refer to its URL later on (see the documentation about + `naming URL patterns`_ for information). We're also using the `url()`_ + function from ``django.conf.urls.defaults`` here. It's a good habit to + use ``url()`` when you are providing a pattern name like this. + +.. _naming URL patterns: ../url_dispatch/#naming-url-patterns +.. _url(): ../url_dispatch/#url + By default, the ``object_detail`` generic view uses a template called ``<app name>/<model name>_detail.html``. In our case, it'll use the template ``"polls/poll_detail.html"``. Thus, rename your ``polls/detail.html`` template to @@ -255,6 +264,15 @@ the new templates and context variables. Change the template call from ``polls/detail.html`` to ``polls/poll_detail.html``, and pass ``object`` in the context instead of ``poll``. +The last thing to do is fix the URL handling to account for the use of generic +views. In the vote view above, we used the ``reverse()`` function to avoid +hard-coding our URLs. Now that we've switched to a generic view, we'll need to +change the ``reverse()`` call to point back to our new generic view. We can't +simply use the view function anymore -- generic views can be (and are) used +multiple times -- but we can use the name we've given:: + + return HttpResponseRedirect(reverse('poll_results', args=(p.id,))) + Run the server, and use your new polling app based on generic views. For full details on generic views, see the `generic views documentation`_. diff --git a/docs/unicode.txt b/docs/unicode.txt new file mode 100644 index 0000000000..1ab255970c --- /dev/null +++ b/docs/unicode.txt @@ -0,0 +1,364 @@ +====================== +Unicode data in Django +====================== + +**New in Django development version** + +Django natively supports Unicode data everywhere. Providing your database can +somehow store the data, you can safely pass around Unicode strings to +templates, models and the database. + +This document tells you what you need to know if you're writing applications +that use data or templates that are encoded in something other than ASCII. + +Creating the database +===================== + +Make sure your database is configured to be able to store arbitrary string +data. Normally, this means giving it an encoding of UTF-8 or UTF-16. If you use +a more restrictive encoding -- for example, latin1 (iso8859-1) -- you won't be +able to store certain characters in the database, and information will be lost. + + * MySQL users, refer to the `MySQL manual`_ (section 10.3.2 for MySQL 5.1) for + details on how to set or alter the database character set encoding. + + * PostgreSQL users, refer to the `PostgreSQL manual`_ (section 21.2.2 in + PostgreSQL 8) for details on creating databases with the correct encoding. + + * SQLite users, there is nothing you need to do. SQLite always uses UTF-8 + for internal encoding. + +.. _MySQL manual: http://www.mysql.org/doc/refman/5.1/en/charset-database.html +.. _PostgreSQL manual: http://www.postgresql.org/docs/8.2/static/multibyte.html#AEN24104 + +All of Django's database backends automatically convert Unicode strings into +the appropriate encoding for talking to the database. They also automatically +convert strings retrieved from the database into Python Unicode strings. You +don't even need to tell Django what encoding your database uses: that is +handled transparently. + +For more, see the section "The database API" below. + +General string handling +======================= + +Whenever you use strings with Django -- e.g., in database lookups, template +rendering or anywhere else -- you have two choices for encoding those strings. +You can use Unicode strings, or you can use normal strings (sometimes called +"bytestrings") that are encoded using UTF-8. + +.. admonition:: Warning + + A bytestring does not carry any information with it about its encoding. + For that reason, we have to make an assumption, and Django assumes that all + bytestrings are in UTF-8. + + If you pass a string to Django that has been encoded in some other format, + things will go wrong in interesting ways. Usually, Django will raise a + ``UnicodeDecodeError`` at some point. + +If your code only uses ASCII data, it's safe to use your normal strings, +passing them around at will, because ASCII is a subset of UTF-8. + +Don't be fooled into thinking that if your ``DEFAULT_CHARSET`` setting is set +to something other than ``'utf-8'`` you can use that other encoding in your +bytestrings! ``DEFAULT_CHARSET`` only applies to the strings generated as +the result of template rendering (and e-mail). Django will always assume UTF-8 +encoding for internal bytestrings. The reason for this is that the +``DEFAULT_CHARSET`` setting is not actually under your control (if you are the +application developer). It's under the control of the person installing and +using your application -- and if that person chooses a different setting, your +code must still continue to work. Ergo, it cannot rely on that setting. + +In most cases when Django is dealing with strings, it will convert them to +Unicode strings before doing anything else. So, as a general rule, if you pass +in a bytestring, be prepared to receive a Unicode string back in the result. + +Translated strings +------------------ + +Aside from Unicode strings and bytestrings, there's a third type of string-like +object you may encounter when using Django. The framework's +internationalization features introduce the concept of a "lazy translation" -- +a string that has been marked as translated but whose actual translation result +isn't determined until the object is used in a string. This feature is useful +in cases where the translation locale is unknown until the string is used, even +though the string might have originally been created when the code was first +imported. + +Normally, you won't have to worry about lazy translations. Just be aware that +if you examine an object and it claims to be a +``django.utils.functional.__proxy__`` object, it is a lazy translation. +Calling ``unicode()`` with the lazy translation as the argument will generate a +Unicode string in the current locale. + +For more details about lazy translation objects, refer to the +internationalization_ documentation. + +.. _internationalization: ../i18n/#lazy-translation + +Useful utility functions +------------------------ + +Because some string operations come up again and again, Django ships with a few +useful functions that should make working with Unicode and bytestring objects +a bit easier. + +Conversion functions +~~~~~~~~~~~~~~~~~~~~ + +The ``django.utils.encoding`` module contains a few functions that are handy +for converting back and forth between Unicode and bytestrings. + + * ``smart_unicode(s, encoding='utf-8', errors='strict')`` converts its + input to a Unicode string. The ``encoding`` parameter specifies the input + encoding. (For example, Django uses this internally when processing form + input data, which might not be UTF-8 encoded.) The ``errors`` parameter + takes any of the values that are accepted by Python's ``unicode()`` + function for its error handling. + + If you pass ``smart_unicode()`` an object that has a ``__unicode__`` + method, it will use that method to do the conversion. + + * ``force_unicode(s, encoding='utf-8', errors='strict')`` is identical to + ``smart_unicode()`` in almost all cases. The difference is when the + first argument is a `lazy translation`_ instance. While + ``smart_unicode()`` preserves lazy translations, ``force_unicode()`` + forces those objects to a Unicode string (causing the translation to + occur). Normally, you'll want to use ``smart_unicode()``. However, + ``force_unicode()`` is useful in template tags and filters that + absolutely *must* have a string to work with, not just something that can + be converted to a string. + + * ``smart_str(s, encoding='utf-8', strings_only=False, errors='strict')`` + is essentially the opposite of ``smart_unicode()``. It forces the first + argument to a bytestring. The ``strings_only`` parameter, if set to True, + will result in Python integers, booleans and ``None`` not being + converted to a string (they keep their original types). This is slightly + different semantics from Python's builtin ``str()`` function, but the + difference is needed in a few places within Django's internals. + +Normally, you'll only need to use ``smart_unicode()``. Call it as early as +possible on any input data that might be either Unicode or a bytestring, and +from then on, you can treat the result as always being Unicode. + +.. _lazy translation: ../i18n/#lazy-translation + +URI and IRI handling +~~~~~~~~~~~~~~~~~~~~ + +Web frameworks have to deal with URLs (which are a type of URI_). One +requirement of URLs is that they are encoded using only ASCII characters. +However, in an international environment, you might need to construct a +URL from an IRI_ -- very loosely speaking, a URI that can contain Unicode +characters. Quoting and converting an IRI to URI can be a little tricky, so +Django provides some assistance. + + * The function ``django.utils.encoding.iri_to_uri()`` implements the + conversion from IRI to URI as required by the specification (`RFC + 3987`_). + + * The functions ``django.utils.http.urlquote()`` and + ``django.utils.http.urlquote_plus()`` are versions of Python's standard + ``urllib.quote()`` and ``urllib.quote_plus()`` that work with non-ASCII + characters. (The data is converted to UTF-8 prior to encoding.) + +These two groups of functions have slightly different purposes, and it's +important to keep them straight. Normally, you would use ``urlquote()`` on the +individual portions of the IRI or URI path so that any reserved characters +such as '&' or '%' are correctly encoded. Then, you apply ``iri_to_uri()`` to +the full IRI and it converts any non-ASCII characters to the correct encoded +values. + +.. note:: + Technically, it isn't correct to say that ``iri_to_uri()`` implements the + full algorithm in the IRI specification. It doesn't (yet) perform the + international domain name encoding portion of the algorithm. + +The ``iri_to_uri()`` function will not change ASCII characters that are +otherwise permitted in a URL. So, for example, the character '%' is not +further encoded when passed to ``iri_to_uri()``. This means you can pass a +full URL to this function and it will not mess up the query string or anything +like that. + +An example might clarify things here:: + + >>> urlquote(u'Paris & Orléans') + u'Paris%20%26%20Orl%C3%A9ans' + >>> iri_to_uri(u'/favorites/François/%s' % urlquote(u'Paris & Orléans')) + '/favorites/Fran%C3%A7ois/Paris%20%26%20Orl%C3%A9ans' + +If you look carefully, you can see that the portion that was generated by +``urlquote()`` in the second example was not double-quoted when passed to +``iri_to_uri()``. This is a very important and useful feature. It means that +you can construct your IRI without worrying about whether it contains +non-ASCII characters and then, right at the end, call ``iri_to_uri()`` on the +result. + +The ``iri_to_uri()`` function is also idempotent, which means the following is +always true:: + + iri_to_uri(iri_to_uri(some_string)) = iri_to_uri(some_string) + +So you can safely call it multiple times on the same IRI without risking +double-quoting problems. + +.. _URI: http://www.ietf.org/rfc/rfc2396.txt +.. _IRI: http://www.ietf.org/rfc/rfc3987.txt +.. _RFC 3987: IRI_ + +Models +====== + +Because all strings are returned from the database as Unicode strings, model +fields that are character based (CharField, TextField, URLField, etc) will +contain Unicode values when Django retrieves data from the database. This +is *always* the case, even if the data could fit into an ASCII bytestring. + +You can pass in bytestrings when creating a model or populating a field, and +Django will convert it to Unicode when it needs to. + +Choosing between ``__str__()`` and ``__unicode__()`` +---------------------------------------------------- + +One consequence of using Unicode by default is that you have to take some care +when printing data from the model. + +In particular, rather than giving your model a ``__str__()`` method, we +recommended you implement a ``__unicode__()`` method. In the ``__unicode__()`` +method, you can quite safely return the values of all your fields without +having to worry about whether they fit into a bytestring or not. (The way +Python works, the result of ``__str__()`` is *always* a bytestring, even if you +accidentally try to return a Unicode object). + +You can still create a ``__str__()`` method on your models if you want, of +course, but you shouldn't need to do this unless you have a good reason. +Django's ``Model`` base class automatically provides a ``__str__()`` +implementation that calls ``__unicode__()`` and encodes the result into UTF-8. +This means you'll normally only need to implement a ``__unicode__()`` method +and let Django handle the coercion to a bytestring when required. + +Taking care in ``get_absolute_url()`` +------------------------------------- + +URLs can only contain ASCII characters. If you're constructing a URL from +pieces of data that might be non-ASCII, be careful to encode the results in a +way that is suitable for a URL. The ``django.db.models.permalink()`` decorator +handles this for you automatically. + +If you're constructing a URL manually (i.e., *not* using the ``permalink()`` +decorator), you'll need to take care of the encoding yourself. In this case, +use the ``iri_to_uri()`` and ``urlquote()`` functions that were documented +above_. For example:: + + from django.utils.encoding import iri_to_uri + from django.utils.http import urlquote + + def get_absolute_url(self): + url = u'/person/%s/?x=0&y=0' % urlquote(self.location) + return iri_to_uri(url) + +This function returns a correctly encoded URL even if ``self.location`` is +something like "Jack visited Paris & Orléans". (In fact, the ``iri_to_uri()`` +call isn't strictly necessary in the above example, because all the +non-ASCII characters would have been removed in quoting in the first line.) + +.. _above: `URI and IRI handling`_ + +The database API +================ + +You can pass either Unicode strings or UTF-8 bytestrings as arguments to +``filter()`` methods and the like in the database API. The following two +querysets are identical:: + + qs = People.objects.filter(name__contains=u'Å') + qs = People.objects.filter(name__contains='\xc3\85') # UTF-8 encoding of Å + +Templates +========= + +You can use either Unicode or bytestrings when creating templates manually:: + + from django.template import Template + t1 = Template('This is a bytestring template.') + t2 = Template(u'This is a Unicode template.') + +But the common case is to read templates from the filesystem, and this creates +a slight complication: not all filesystems store their data encoded as UTF-8. +If your template files are not stored with a UTF-8 encoding, set the ``FILE_CHARSET`` +setting to the encoding of the files on disk. When Django reads in a template +file, it will convert the data from this encoding to Unicode. (``FILE_CHARSET`` +is set to ``'utf-8'`` by default.) + +The ``DEFAULT_CHARSET`` setting controls the encoding of rendered templates. +This is set to UTF-8 by default. + +Template tags and filters +------------------------- + +A couple of tips to remember when writing your own template tags and filters: + + * Always return Unicode strings from a template tag's ``render()`` method + and from template filters. + + * Use ``force_unicode()`` in preference to ``smart_unicode()`` in these + places. Tag rendering and filter calls occur as the template is being + rendered, so there is no advantage to postponing the conversion of lazy + translation objects into strings. It's easier to work solely with Unicode + strings at that point. + +E-mail +====== + +Django's e-mail framework (in ``django.core.mail``) supports Unicode +transparently. You can use Unicode data in the message bodies and any headers. +However, you're still obligated to respect the requirements of the e-mail +specifications, so, for example, e-mail addresses should use only ASCII +characters. + +The following code example demonstrates that everything except e-mail addresses +can be non-ASCII:: + + from django.core.mail import EmailMessage + + subject = u'My visit to Sør-Trøndelag' + sender = u'Arnbjörg Ráðormsdóttir <arnbjorg@example.com>' + recipients = ['Fred <fred@example.com'] + body = u'...' + EmailMessage(subject, body, sender, recipients).send() + +Form submission +=============== + +HTML form submission is a tricky area. There's no guarantee that the +submission will include encoding information, which means the framework might +have to guess at the encoding of submitted data. + +Django adopts a "lazy" approach to decoding form data. The data in an +``HttpRequest`` object is only decoded when you access it. In fact, most of +the data is not decoded at all. Only the ``HttpRequest.GET`` and +``HttpRequest.POST`` data structures have any decoding applied to them. Those +two fields will return their members as Unicode data. All other attributes and +methods of ``HttpRequest`` return data exactly as it was submitted by the +client. + +By default, the ``DEFAULT_CHARSET`` setting is used as the assumed encoding +for form data. If you need to change this for a particular form, you can set +the ``encoding`` attribute on an ``HttpRequest`` instance. For example:: + + def some_view(request): + # We know that the data must be encoded as KOI8-R (for some reason). + request.encoding = 'koi8-r' + ... + +You can even change the encoding after having accessed ``request.GET`` or +``request.POST``, and all subsequent accesses will use the new encoding. + +Most developers won't need to worry about changing form encoding, but this is +a useful feature for applications that talk to legacy systems whose encoding +you cannot control. + +Django does not decode the data of file uploads, because that data is normally +treated as collections of bytes, rather than strings. Any automatic decoding +there would alter the meaning of the stream of bytes. diff --git a/docs/url_dispatch.txt b/docs/url_dispatch.txt index 402b1200b5..76a4e1b5f0 100644 --- a/docs/url_dispatch.txt +++ b/docs/url_dispatch.txt @@ -204,8 +204,16 @@ optional extra arguments dictionary. For example:: ... ) +This function takes five arguments, most of which are optional:: + + url(regex, view, kwargs=None, name=None, prefix='') + See `Naming URL patterns`_ for why the ``name`` parameter is useful. +The ``prefix`` parameter has the same meaning as the first argument to +``patterns()`` and is only relevant when you're passing a string as the +``view`` parameter. + handler404 ---------- @@ -317,7 +325,7 @@ Old:: from django.conf.urls.defaults import * urlpatterns = patterns('', - (r'^/?$', 'django.views.generic.date_based.archive_index'), + (r'^$', 'django.views.generic.date_based.archive_index'), (r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$', 'django.views.generic.date_based.archive_month'), (r'^tag/(?P<tag>\w+)/$', 'weblog.views.tag'), ) @@ -327,7 +335,7 @@ New:: from django.conf.urls.defaults import * urlpatterns = patterns('django.views.generic.date_based', - (r'^/?$', 'archive_index'), + (r'^$', 'archive_index'), (r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$','archive_month'), ) @@ -392,7 +400,7 @@ dictionary of extra keyword arguments to pass to the view function. For example:: urlpatterns = patterns('blog.views', - (r'^/blog/(?P<year>\d{4})/$', 'year_archive', {'foo': 'bar'}), + (r'^blog/(?P<year>\d{4})/$', 'year_archive', {'foo': 'bar'}), ) In this example, for a request to ``/blog/2005/``, Django will call the @@ -404,7 +412,7 @@ This technique is used in `generic views`_ and in the `syndication framework`_ to pass metadata and options to views. .. _generic views: ../generic_views/ -.. _syndication framework: ../syndication/ +.. _syndication framework: ../syndication_feeds/ .. admonition:: Dealing with conflicts @@ -512,7 +520,7 @@ view:: This is completely valid, but it leads to problems when you try to do reverse URL matching (through the ``permalink()`` decorator or the ``{% url %}`` -template tag). Continuing this example, if you wanted to retrieve the URL for +`template tag`_). Continuing this example, if you wanted to retrieve the URL for the ``archive`` view, Django's reverse URL matcher would get confused, because *two* URLpatterns point at that view. @@ -552,14 +560,16 @@ not restricted to valid Python names. name, will decrease the chances of collision. We recommend something like ``myapp-comment`` instead of ``comment``. +.. _template tag: ../templates/#url + Utility methods =============== reverse() --------- -If you need to use something similar to the ``{% url %}`` template tag in your -code, Django provides the ``django.core.urlresolvers.reverse()``. The +If you need to use something similar to the ``{% url %}`` `template tag`_ in +your code, Django provides the ``django.core.urlresolvers.reverse()``. The ``reverse()`` function has the following signature:: reverse(viewname, urlconf=None, args=None, kwargs=None) |
