diff options
Diffstat (limited to 'docs')
45 files changed, 4759 insertions, 3985 deletions
diff --git a/docs/add_ons.txt b/docs/add_ons.txt index 029e314f12..2b2de76cba 100644 --- a/docs/add_ons.txt +++ b/docs/add_ons.txt @@ -23,9 +23,10 @@ admin ===== The automatic Django administrative interface. For more information, see -`Tutorial 2`_. +`Tutorial 2`_ and the `admin documentation`_. .. _Tutorial 2: ../tutorial02/ +.. _admin documentation: ../admin/ Requires the auth_ and contenttypes_ contrib packages to be installed. @@ -76,7 +77,7 @@ Requires the sites_ contrib package to be installed as well. formtools ========= -A set of high-level abstractions for Django forms (django.newforms). +A set of high-level abstractions for Django forms (django.forms). django.contrib.formtools.preview -------------------------------- diff --git a/docs/admin.txt b/docs/admin.txt index fbdd19bc90..82d85a6f5f 100644 --- a/docs/admin.txt +++ b/docs/admin.txt @@ -18,19 +18,21 @@ Django's admin interface. Overview ======== -There are four steps in activating the Django admin site: +There are five steps in activating the Django admin site: - 1. Determine which of your application's models should be editable in the + 1. Add ``django.contrib.admin`` to your ``INSTALLED_APPS`` setting. + + 2. Determine which of your application's models should be editable in the admin interface. - 2. For each of those models, optionally create a ``ModelAdmin`` class that + 3. For each of those models, optionally create a ``ModelAdmin`` class that encapsulates the customized admin functionality and options for that particular model. - 3. Instantiate an ``AdminSite`` and tell it about each of your models and + 4. Instantiate an ``AdminSite`` and tell it about each of your models and ``ModelAdmin`` classes. - 4. Hook the ``AdminSite`` instance into your URLconf. + 5. Hook the ``AdminSite`` instance into your URLconf. ``ModelAdmin`` objects ====================== @@ -41,7 +43,7 @@ Let's take a look at a very simple example the ``ModelAdmin``:: from django.contrib import admin from myproject.myapp.models import Author - + class AuthorAdmin(admin.ModelAdmin): pass admin.site.register(Author, AuthorAdmin) @@ -67,6 +69,13 @@ Example:: date_hierarchy = 'pub_date' +``form`` +~~~~~~~~ + +The default ``forms.ModelForm`` class used to generate the form on the +add/change pages for models. You can easily change this to your own +``ModelForm`` to override the default form behavior of the add/change pages. + ``fieldsets`` ~~~~~~~~~~~~~ @@ -82,7 +91,7 @@ dictionary of information about the fieldset, including a list of fields to be displayed in it. A full example, taken from the ``django.contrib.flatpages.FlatPage`` model:: - + class FlatPageAdmin(admin.ModelAdmin): fieldsets = ( (None, { @@ -106,9 +115,9 @@ The ``field_options`` dictionary can have the following keys: ``fields`` A tuple of field names to display in this fieldset. This key is required. - + Example:: - + { 'fields': ('first_name', 'last_name', 'address', 'city', 'state'), } @@ -116,26 +125,20 @@ The ``field_options`` dictionary can have the following keys: To display multiple fields on the same line, wrap those fields in their own tuple. In this example, the ``first_name`` and ``last_name`` fields will display on the same line:: - + { 'fields': (('first_name', 'last_name'), 'address', 'city', 'state'), } ``classes`` - A string containing extra CSS classes to apply to the fieldset. - + A list containing extra CSS classes to apply to the fieldset. + Example:: - - { - 'classes': 'wide', - } - Apply multiple classes by separating them with spaces. Example:: - { - 'classes': 'wide extrapretty', + 'classes': ['wide', 'extrapretty'], } - + Two useful classes defined by the default admin-site stylesheet are ``collapse`` and ``wide``. Fieldsets with the ``collapse`` style will be initially collapsed in the admin and replaced with a small "click to expand" @@ -143,13 +146,38 @@ The ``field_options`` dictionary can have the following keys: ``description`` A string of optional extra text to be displayed at the top of each fieldset, - under the heading of the fieldset. It's used verbatim, so you can use any HTML - and you must escape any special HTML characters (such as ampersands) yourself. + under the heading of the fieldset. + + Note that this value is *not* HTML-escaped when it's displayed in + the admin interface. This lets you include HTML if you so desire. + Alternatively you can use plain text and + ``django.utils.html.escape()`` to escape any HTML special + characters. + +``fields`` +~~~~~~~~~~ + +Use this option as an alternative to ``fieldsets`` if the layout does not +matter and if you want to only show a subset of the available fields in the +form. For example, you could define a simpler version of the admin form for +the ``django.contrib.flatpages.FlatPage`` model as follows:: + + class FlatPageAdmin(admin.ModelAdmin): + fields = ('url', 'title', 'content') + +In the above example, only the fields 'url', 'title' and 'content' will be +displayed, sequencially, in the form. + +.. admonition:: Note + + This ``fields`` option should not be confused with the ``fields`` + dictionary key that is within the ``fieldsets`` option, as described in + the previous section. ``filter_horizontal`` ~~~~~~~~~~~~~~~~~~~~~ -Use a nifty unobtrusive Javascript "filter" interface instead of the +Use a nifty unobtrusive JavaScript "filter" interface instead of the usability-challenged ``<select multiple>`` in the admin form. The value is a list of fields that should be displayed as a horizontal filter interface. See ``filter_vertical`` to use a vertical interface. @@ -192,7 +220,7 @@ A few special cases to note about ``list_display``: function attribute, for use as the header for the field. Here's a full example model:: - + class Person(models.Model): name = models.CharField(max_length=50) birthday = models.DateField() @@ -200,7 +228,7 @@ A few special cases to note about ``list_display``: def decade_born_in(self): return self.birthday.strftime('%Y')[:3] + "0's" decade_born_in.short_description = 'Birth decade' - + class PersonAdmin(admin.ModelAdmin): list_display = ('name', 'decade_born_in') @@ -218,7 +246,7 @@ A few special cases to note about ``list_display``: def colored_name(self): return '<span style="color: #%s;">%s %s</span>' % (self.color_code, self.first_name, self.last_name) colored_name.allow_tags = True - + class PersonAdmin(admin.ModelAdmin): list_display = ('first_name', 'last_name', 'colored_name') @@ -235,7 +263,7 @@ A few special cases to note about ``list_display``: def born_in_fifties(self): return self.birthday.strftime('%Y')[:3] == 5 born_in_fifties.boolean = True - + class PersonAdmin(admin.ModelAdmin): list_display = ('name', 'born_in_fifties') @@ -264,7 +292,7 @@ A few special cases to note about ``list_display``: return '<span style="color: #%s;">%s</span>' % (self.color_code, self.first_name) colored_first_name.allow_tags = True colored_first_name.admin_order_field = 'first_name' - + class PersonAdmin(admin.ModelAdmin): list_display = ('first_name', 'colored_first_name') @@ -355,6 +383,11 @@ ordered. This should be a list or tuple in the same format as a model's If this isn't provided, the Django admin will use the model's default ordering. +.. admonition:: Note + + Django will only honor the first element in the list/tuple; any others + will be ignored. + ``prepopulated_fields`` ~~~~~~~~~~~~~~~~~~~~~~~ @@ -364,11 +397,15 @@ it should prepopulate from:: class ArticleAdmin(admin.ModelAdmin): prepopulated_fields = {"slug": ("title",)} -When set the given fields will use a bit of Javascript to populate from the -fields assigned. +When set, the given fields will use a bit of JavaScript to populate from the +fields assigned. The main use for this functionality is to automatically +generate the value for ``SlugField`` fields from one or more other fields. The +generated value is produced by concatenating the values of the source fields, +and then by transforming that result into a valid slug (e.g. substituting +dashes for spaces). -``prepopulated_fields`` doesn't accept DateTimeFields, ForeignKeys nor -ManyToManyFields. +``prepopulated_fields`` doesn't accept ``DateTimeField``, ``ForeignKey``, nor +``ManyToManyField`` fields. ``radio_fields`` ~~~~~~~~~~~~~~~~ @@ -396,7 +433,10 @@ overhead of having to select all the related instances to display in the drop-down. ``raw_id_fields`` is a list of fields you would like to change -into a ``Input`` widget for the primary key. +into a ``Input`` widget for either a ``ForeignKey`` or ``ManyToManyField``:: + + class ArticleAdmin(admin.ModelAdmin): + raw_id_fields = ("newspaper",) ``save_as`` ~~~~~~~~~~~ @@ -484,7 +524,7 @@ with an operator: ``ModelAdmin`` media definitions -------------------------------- -There are times where you would like add a bit of CSS and/or Javascript to +There are times where you would like add a bit of CSS and/or JavaScript to the add/change views. This can be accomplished by using a Media inner class on your ``ModelAdmin``:: @@ -498,24 +538,49 @@ on your ``ModelAdmin``:: Keep in mind that this will be prepended with ``MEDIA_URL``. The same rules apply as `regular media definitions on forms`_. -.. _regular media definitions on forms: ../newforms/#media +.. _regular media definitions on forms: ../forms/#media + +Adding custom validation to the admin +------------------------------------- + +Adding custom validation of data in the admin is quite easy. The automatic +admin interfaces reuses the Django `forms`_ module. The ``ModelAdmin`` class +gives you the ability define your own form:: + + class ArticleAdmin(admin.ModelAdmin): + form = MyArticleAdminForm + +``MyArticleAdminForm`` can be defined anywhere as long as you import where +needed. Now within your form you can add your own custom validation for +any field:: + + class MyArticleAdminForm(forms.ModelForm): + def clean_name(self): + # do something that validates your data + return self.cleaned_data["name"] + +It is important you use a ``ModelForm`` here otherwise things can break. See +the `forms`_ documentation on `custom validation`_ for more information. + +.. _forms: ../forms/ +.. _custom validation: ../forms/#custom-form-and-field-validation ``InlineModelAdmin`` objects ============================ The admin interface has the ability to edit models on the same page as a -parent model. These are called inlines. You can add them a model being -specifing them in a ``ModelAdmin.inlines`` attribute:: +parent model. These are called inlines. You can add them to a model by +specifying them in a ``ModelAdmin.inlines`` attribute:: class BookInline(admin.TabularInline): model = Book - + class AuthorAdmin(admin.ModelAdmin): inlines = [ BookInline, ] -Django provides two subclasses of ``InlineModelAdmin`` and they are:: +Django provides two subclasses of ``InlineModelAdmin`` and they are: * ``TabularInline`` * ``StackedInline`` @@ -562,7 +627,7 @@ inline. This controls the number of extra forms the formset will display in addition to the initial forms. See the `formsets documentation`_ for more information. -.. _formsets documentation: ../newforms/#formsets +.. _formsets documentation: ../forms/#formsets ``max_num`` ~~~~~~~~~~~ @@ -573,6 +638,21 @@ enough. See `max_num in formsets`_ for more information. .. _max_num in formsets: ../modelforms/#limiting-the-number-of-objects-editable +``raw_id_fields`` +~~~~~~~~~~~~~~~~~ + +By default, Django's admin uses a select-box interface (<select>) for +fields that are ``ForeignKey``. Sometimes you don't want to incur the +overhead of having to select all the related instances to display in the +drop-down. + +``raw_id_fields`` is a list of fields you would like to change +into a ``Input`` widget for either a ``ForeignKey`` or ``ManyToManyField``:: + + class BookInline(admin.TabularInline): + model = Book + raw_id_fields = ("pages",) + ``template`` ~~~~~~~~~~~~ @@ -598,7 +678,7 @@ Take this model for instance:: class Friendship(models.Model): to_person = models.ForeignKey(Person, related_name="friends") from_person = models.ForeignKey(Person, related_name="from_friends") - + If you wanted to display an inline on the ``Person`` admin add/change pages you need to explicitly define the foreign key since it is unable to do so automatically:: @@ -606,12 +686,67 @@ automatically:: class FriendshipInline(admin.TabularInline): model = Friendship fk_name = "to_person" - + class PersonAdmin(admin.ModelAdmin): inlines = [ FriendshipInline, ] +Working with Many-to-Many Intermediary Models +---------------------------------------------- + +By default, admin widgets for many-to-many relations will be displayed inline +on whichever model contains the actual reference to the ``ManyToManyField``. +However, when you specify an intermediary model using the ``through`` +argument to a ``ManyToManyField``, the admin will not display a widget by +default. This is because each instance of that intermediary model requires +more information than could be displayed in a single widget, and the layout +required for multiple widgets will vary depending on the intermediate model. + +However, we still want to be able to edit that information inline. Fortunately, +this is easy to do with inline admin models. Suppose we have the following +models:: + + class Person(models.Model): + name = models.CharField(max_length=128) + + class Group(models.Model): + name = models.CharField(max_length=128) + members = models.ManyToManyField(Person, through='Membership') + + class Membership(models.Model): + person = models.ForeignKey(Person) + group = models.ForeignKey(Group) + date_joined = models.DateField() + invite_reason = models.CharField(max_length=64) + +The first step in displaying this intermediate model in the admin is to +define an inline class for the ``Membership`` model:: + + class MembershipInline(admin.TabularInline): + model = Membership + extra = 1 + +This simple example uses the default ``InlineModelAdmin`` values for the +``Membership`` model, and limits the extra add forms to one. This could be +customized using any of the options available to ``InlineModelAdmin`` classes. + +Now create admin views for the ``Person`` and ``Group`` models:: + + class PersonAdmin(admin.ModelAdmin): + inlines = (MembershipInline,) + + class GroupAdmin(admin.ModelAdmin): + inlines = (MembershipInline,) + +Finally, register your ``Person`` and ``Group`` models with the admin site:: + + admin.site.register(Person, PersonAdmin) + admin.site.register(Group, GroupAdmin) + +Now your admin site is set up to edit ``Membership`` objects inline from +either the ``Person`` or the ``Group`` detail pages. + ``AdminSite`` objects ===================== @@ -628,7 +763,7 @@ In this example, we register the default ``AdminSite`` instance # urls.py from django.conf.urls.defaults import * from django.contrib import admin - + admin.autodiscover() urlpatterns = patterns('', diff --git a/docs/api_stability.txt b/docs/api_stability.txt index 769359b75e..f56872ca51 100644 --- a/docs/api_stability.txt +++ b/docs/api_stability.txt @@ -59,7 +59,7 @@ These APIs are stable: - `Request/response objects`_. - - `Sending email`_. + - `Sending e-mail`_. - `Sessions`_. @@ -108,13 +108,12 @@ change: .. _mod_python integration: ../modpython/ .. _redirects: ../redirects/ .. _request/response objects: ../request_response/ -.. _sending email: ../email/ +.. _sending e-mail: ../email/ .. _sessions: ../sessions/ .. _settings: ../settings/ .. _syndication: ../syndication_feeds/ .. _template language: ../templates/ .. _transactions: ../transactions/ .. _url dispatch: ../url_dispatch/ -.. _forms and validation: ../forms/ .. _serialization: ../serialization/ .. _authentication: ../authentication/ diff --git a/docs/authentication.txt b/docs/authentication.txt index cd76731bc4..acd378fcab 100644 --- a/docs/authentication.txt +++ b/docs/authentication.txt @@ -517,7 +517,7 @@ It's your responsibility to provide the login form in a template called template context variables: * ``form``: A ``Form`` object representing the login form. See the - `newforms documentation`_ for more on ``Form`` objects. + `forms documentation`_ for more on ``FormWrapper`` objects. * ``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 @@ -557,7 +557,7 @@ block:: {% endblock %} -.. _newforms documentation: ../newforms/ +.. _forms documentation: ../forms/ .. _site framework docs: ../sites/ Other built-in views @@ -631,7 +631,7 @@ The page shown after a user has changed their password. **Description:** Allows a user to reset their password, and sends them the new password -in an email. +in an e-mail. **Optional arguments:** @@ -640,7 +640,7 @@ in an email. ``registration/password_reset_form.html`` if not supplied. * ``email_template_name``: The full name of a template to use for - generating the email with the new password. This will default to + generating the e-mail with the new password. This will default to ``registration/password_reset_email.html`` if not supplied. **Template context:** @@ -696,7 +696,7 @@ system provides several built-in forms: user to change their password. * ``django.contrib.auth.forms.PasswordResetForm``: A form for resetting a - user's password and emailing the new password to them. + user's password and e-mailing the new password to them. * ``django.contrib.auth.forms.UserCreationForm``: A form for creating a new user. diff --git a/docs/cache.txt b/docs/cache.txt index 3318b2ad4a..d22dd91994 100644 --- a/docs/cache.txt +++ b/docs/cache.txt @@ -6,8 +6,8 @@ A fundamental tradeoff in dynamic Web sites is, well, they're dynamic. Each time a user requests a page, the Web server makes all sorts of calculations -- from database queries to template rendering to business logic -- to create the page that your site's visitor sees. This is a lot more expensive, from a -processing-overhead perspective, than your standard read-a-file-off-the-filesystem -server arrangement. +processing-overhead perspective, than your standard +read-a-file-off-the-filesystem server arrangement. For most Web applications, this overhead isn't a big deal. Most Web applications aren't washingtonpost.com or slashdot.org; they're simply small- @@ -159,19 +159,6 @@ cache is multi-process and thread-safe. To use it, set ``CACHE_BACKEND`` to CACHE_BACKEND = 'locmem:///' -Simple caching (for development) --------------------------------- - -A simple, single-process memory cache is available as ``"simple:///"``. This -merely saves cached data in-process, which means it should only be used in -development or testing environments. For example:: - - CACHE_BACKEND = 'simple:///' - -**New in Django development version:** This cache backend is deprecated and -will be removed in a future release. New code should use the ``locmem`` backend -instead. - Dummy caching (for development) ------------------------------- @@ -186,6 +173,27 @@ production environment still will. To activate dummy caching, set CACHE_BACKEND = 'dummy:///' +Using a custom cache backend +---------------------------- + +**New in Django development version** + +While Django includes support for a number of cache backends out-of-the-box, +sometimes you will want to use a customised verison or your own backend. To +use an external cache backend with Django, use a Python import path as the +scheme portion (the part before the initial colon) of the ``CACHE_BACKEND`` +URI, like so:: + + CACHE_BACKEND = 'path.to.backend://' + +If you're building your own backend, you can use the standard cache backends +as reference implementations. You'll find the code in the +``django/core/cache/backends/`` directory of the Django source. + +Note: Without a really compelling reason, like a host that doesn't support the +them, you should stick to the cache backends included with Django. They've +been really well-tested and are quite easy to use. + CACHE_BACKEND arguments ----------------------- diff --git a/docs/contenttypes.txt b/docs/contenttypes.txt index 96e3b0939c..a4fc045714 100644 --- a/docs/contenttypes.txt +++ b/docs/contenttypes.txt @@ -205,10 +205,10 @@ model. There are three parts to setting up a ``GenericForeignKey``: models you'll be relating to. (For most models, this means an ``IntegerField`` or ``PositiveIntegerField``.) - This field must be of the same type as the primary key of the models - that will be involved in the generic relation. For example, if you use - ``IntegerField``, you won't be able to form a generic relation with a - model that uses a ``CharField`` as a primary key. + This field must be of the same type as the primary key of the models + that will be involved in the generic relation. For example, if you use + ``IntegerField``, you won't be able to form a generic relation with a + model that uses a ``CharField`` as a primary key. 3. Give your model a ``GenericForeignKey``, and pass it the names of the two fields described above. If these fields are named diff --git a/docs/contributing.txt b/docs/contributing.txt index f3bee14069..d0d56a0e39 100644 --- a/docs/contributing.txt +++ b/docs/contributing.txt @@ -525,7 +525,6 @@ Model style * All database fields * ``class Meta`` - * ``class Admin`` * ``def __unicode__()`` * ``def __str__()`` * ``def save()`` diff --git a/docs/csrf.txt b/docs/csrf.txt index 7d79e39502..ba04fa67cd 100644 --- a/docs/csrf.txt +++ b/docs/csrf.txt @@ -4,8 +4,8 @@ Cross Site Request Forgery protection The CsrfMiddleware class provides easy-to-use protection against `Cross Site Request Forgeries`_. This type of attack occurs when a malicious -web site creates a link or form button that is intended to perform some action -on your web site, using the credentials of a logged-in user who is tricked +Web site creates a link or form button that is intended to perform some action +on your Web site, using the credentials of a logged-in user who is tricked into clicking on the link in their browser. The first defense against CSRF attacks is to ensure that GET requests @@ -38,7 +38,7 @@ CsrfMiddleware does two things: checks that the 'csrfmiddlewaretoken' is present and correct. If it isn't, the user will get a 403 error. -This ensures that only forms that have originated from your web site +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 @@ -47,7 +47,7 @@ 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 +but they do not need to be protected, since the 'attacking' Web site could make these kind of requests anyway. The Content-Type is checked before modifying the response, and only @@ -64,8 +64,8 @@ a custom authentication system that manually sets cookies and the like, it won't help you. If your app creates HTML pages and forms in some unusual way, (e.g. -it sends fragments of HTML in javascript document.write statements) +it sends fragments of HTML in JavaScript document.write statements) you might bypass the filter that adds the hidden field to the form, in which case form submission will always fail. It may still be possible to use the middleware, provided you can find some way to get the -CSRF token and ensure that is included when your form is submitted.
\ No newline at end of file +CSRF token and ensure that is included when your form is submitted. diff --git a/docs/custom_model_fields.txt b/docs/custom_model_fields.txt index cbaac873e3..6b8f3c3ac6 100644 --- a/docs/custom_model_fields.txt +++ b/docs/custom_model_fields.txt @@ -111,7 +111,7 @@ into the precise details of what ``Field`` can do later on; for now, suffice it to say that everything descends from ``Field`` and then customizes key pieces of the class behavior. -.. _form fields: ../newforms/#fields +.. _form fields: ../forms/#fields It's important to realize that a Django field class is not what is stored in your model attributes. The model attributes contain normal Python objects. The @@ -385,8 +385,8 @@ Python object type we want to store in the model's attribute. called when it is created, you should be using `The SubfieldBase metaclass`_ mentioned earlier. Otherwise ``to_python()`` won't be called automatically. -``get_db_prep_save(self, value)`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +``get_db_prep_value(self, value)`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This is the reverse of ``to_python()`` when working with the database backends (as opposed to serialization). The ``value`` parameter is the current value of @@ -399,10 +399,20 @@ For example:: class HandField(models.Field): # ... - def get_db_prep_save(self, value): + def get_db_prep_value(self, value): return ''.join([''.join(l) for l in (value.north, value.east, value.south, value.west)]) +``get_db_prep_save(self, value)`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Same as the above, but called when the Field value must be *saved* to the +database. As the default implementation just calls ``get_db_prep_value``, you +shouldn't need to implement this method unless your custom field need a special +conversion when being saved that is not the same as the used for normal query +parameters (which is implemented by ``get_db_prep_value``). + + ``pre_save(self, model_instance, add)`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -440,14 +450,21 @@ by with handling the lookup types that need special handling for your field and pass the rest of the ``get_db_prep_lookup()`` method of the parent class. If you needed to implement ``get_db_prep_save()``, you will usually need to -implement ``get_db_prep_lookup()``. The usual reason is because of the -``range`` and ``in`` lookups. In these case, you will passed a list of -objects (presumably of the right type) and will need to convert them to a list -of things of the right type for passing to the database. Sometimes you can -reuse ``get_db_prep_save()``, or at least factor out some common pieces from -both methods into a help function. +implement ``get_db_prep_lookup()``. If you don't, ``get_db_prep_value`` will be +called by the default implementation, to manage ``exact``, ``gt``, ``gte``, +``lt``, ``lte``, ``in`` and ``range`` lookups. -For example:: +You may also want to implement this method to limit the lookup types that could +be used with your custom field type. + +Note that, for ``range`` and ``in`` lookups, ``get_db_prep_lookup`` will receive +a list of objects (presumably of the right type) and will need to convert them +to a list of things of the right type for passing to the database. Most of the +time, you can reuse ``get_db_prep_value()``, or at least factor out some common +pieces. + +For example, the following code implements ``get_db_prep_lookup`` to limit the +accepted lookup types to ``exact`` and ``in``:: class HandField(models.Field): # ... @@ -455,9 +472,9 @@ For example:: def get_db_prep_lookup(self, lookup_type, value): # We only handle 'exact' and 'in'. All others are errors. if lookup_type == 'exact': - return self.get_db_prep_save(value) + return self.get_db_prep_value(value) elif lookup_type == 'in': - return [self.get_db_prep_save(v) for v in value] + return [self.get_db_prep_value(v) for v in value] else: raise TypeError('Lookup type %r not supported.' % lookup_type) @@ -493,8 +510,8 @@ This assumes we're imported a ``MyFormField`` field class (which has its own default widget). This document doesn't cover the details of writing custom form fields. -.. _helper functions: ../newforms/#generating-forms-for-models -.. _forms documentation: ../newforms/ +.. _helper functions: ../forms/#generating-forms-for-models +.. _forms documentation: ../forms/ ``get_internal_type(self)`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -538,11 +555,11 @@ serializer output in some other place, outside of Django. Although implementing this method is necessary to allow field serialization, the API might change in the future. -Returns a dictionary, mapping the field's attribute name to a flattened string -version of the data. This method has some internal uses that aren't of -interest to use here (mostly having to do with manipulators). For our -purposes, it's sufficient to return a one item dictionary that maps the -attribute name to a string. +Returns a dictionary, mapping the field's attribute name to a +flattened string version of the data. This method has some internal +uses that aren't of interest to use here (mostly having to do with +forms). For our purposes, it's sufficient to return a one item +dictionary that maps the attribute name to a string. This method is used by the serializers to convert the field into a string for output. You can ignore the input parameters for serialization purposes, @@ -557,7 +574,7 @@ we can reuse some existing conversion code:: def flatten_data(self, follow, obj=None): value = self._get_val_from_obj(obj) - return {self.attname: self.get_db_prep_save(value)} + return {self.attname: self.get_db_prep_value(value)} Some general advice -------------------- diff --git a/docs/databases.txt b/docs/databases.txt index 6832c2b361..0fb6381c5c 100644 --- a/docs/databases.txt +++ b/docs/databases.txt @@ -192,8 +192,8 @@ database user must have privileges to run the following commands: To run Django's test suite, the user needs these *additional* privileges: - * CREATE DATABASE - * DROP DATABASE + * CREATE USER + * DROP USER * CREATE TABLESPACE * DROP TABLESPACE diff --git a/docs/db-api.txt b/docs/db-api.txt index 5fdcd946bd..f62d5c6d57 100644 --- a/docs/db-api.txt +++ b/docs/db-api.txt @@ -463,7 +463,7 @@ Be careful, if you are using ``extra()`` to add custom handling to your may or may not make sense. If you are using custom SQL fragments in your ``extra()`` calls, Django will not inspect these fragments to see if they need to be rewritten because of changes in the merged query. So test the effects -carefully. Also realise that if you are combining two ``QuerySets`` with +carefully. Also realize that if you are combining two ``QuerySets`` with ``|``, you cannot use ``extra(select=...)`` or ``extra(where=...)`` on *both* ``QuerySets``. You can only use those calls on one or the other (Django will raise a ``ValueError`` if you try to use this incorrectly). @@ -1676,6 +1676,28 @@ whose ``headline`` contains ``'Lennon'``:: Blog.objects.filter(entry__headline__contains='Lennon') +If you are filtering across multiple relationships and one of the intermediate +models doesn't have a value that meets the filter condition, Django will treat +it as if there is an empty (all values are ``NULL``), but valid, object there. +All this means is that no error will be raised. For example, in this filter:: + + Blog.objects.filter(entry__author__name='Lennon') + +(if there was a related ``Author`` model), if there was no ``author`` +associated with an entry, it would be treated as if there was also no ``name`` +attached, rather than raising an error because of the missing ``author``. +Usually this is exactly what you want to have happen. The only case where it +might be confusing is if you are using ``isnull``. Thus:: + + Blog.objects.filter(entry__author__name__isnull=True) + +will return ``Blog`` objects that have an empty ``name`` on the ``author`` and +also those which have an empty ``author`` on the ``entry``. If you don't want +those latter objects, you could write:: + + Blog.objetcs.filter(entry__author__isnull=False, + entry__author__name__isnull=True) + Spanning multi-valued relationships ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -2218,7 +2240,7 @@ statement. It is a bulk operation for direct updates. It doesn't run any signals (which are a consequence of calling ``save()``). If you want to save every item in a ``QuerySet`` and make sure that the ``save()`` method is called on each instance, you don't need any special function to handle that. -Just loop over them and call ``save()``: +Just loop over them and call ``save()``:: for item in my_queryset: item.save() @@ -2280,6 +2302,11 @@ For every ``FileField``, the object will have a ``get_FOO_filename()`` method, where ``FOO`` is the name of the field. This returns the full filesystem path to the file, according to your ``MEDIA_ROOT`` setting. +.. note:: + It is only valid to call this method **after** saving the model when the + field has been set. Prior to saving, the value returned will not contain + the upload directory (the `upload_to` parameter) in the path. + Note that ``ImageField`` is technically a subclass of ``FileField``, so every model with an ``ImageField`` will also get this method. @@ -2291,6 +2318,11 @@ where ``FOO`` is the name of the field. This returns the full URL to the file, according to your ``MEDIA_URL`` setting. If the value is blank, this method returns an empty string. +.. note:: + As with ``get_FOO_filename()``, it is only valid to call this method + **after** saving the model, otherwise an incorrect result will be + returned. + get_FOO_size() -------------- diff --git a/docs/django-admin.txt b/docs/django-admin.txt index f5cc02ee52..71c4cf545a 100644 --- a/docs/django-admin.txt +++ b/docs/django-admin.txt @@ -395,7 +395,7 @@ makemessages Runs over the entire source tree of the current directory and pulls out all strings marked for translation. It creates (or updates) a message file in the -conf/locale (in the django tree) or locale (for project and application) +conf/locale (in the Django tree) or locale (for project and application) directory. After making changes to the messages files you need to compile them with ``compilemessages`` for use with the builtin gettext support. See the `i18n documentation`_ for details. diff --git a/docs/email.txt b/docs/email.txt index 7f2eef6fc7..e54620c818 100644 --- a/docs/email.txt +++ b/docs/email.txt @@ -179,7 +179,7 @@ from the request's POST data, sends that to admin@example.com and redirects to return HttpResponse('Invalid header found.') return HttpResponseRedirect('/contact/thanks/') else: - # In reality we'd use a manipulator + # In reality we'd use a form class # to get proper validation errors. return HttpResponse('Make sure all fields are entered and valid.') diff --git a/docs/faq.txt b/docs/faq.txt index 6d12a95cba..2c1ffa72db 100644 --- a/docs/faq.txt +++ b/docs/faq.txt @@ -224,19 +224,10 @@ means!). When will you release Django 1.0? --------------------------------- -Short answer: When we're comfortable with Django's APIs, have added all -features that we feel are necessary to earn a "1.0" status, and are ready to -begin maintaining backwards compatibility. +See our `version one roadmap`_ for the detailed timeline. We're aiming for +September 2, 2008. -The merging of Django's `Queryset Refactor branch`_ went a long way toward Django -1.0. Merging the `Newforms Admin branch` will be another important step. - -Of course, you should note that `quite a few production sites`_ use Django in -its current status. Don't let the lack of a 1.0 turn you off. - -.. _Queryset Refactor branch: http://code.djangoproject.com/wiki/QuerysetRefactorBranch -.. _Newforms Admin branch: http://code.djangoproject.com/wiki/NewformsAdminBranch -.. _quite a few production sites: http://code.djangoproject.com/wiki/DjangoPoweredSites +.. _version one roadmap: http://code.djangoproject.com/wiki/VersionOneRoadmap How can I download the Django documentation to read it offline? --------------------------------------------------------------- @@ -434,7 +425,7 @@ Using a ``FileField`` or an ``ImageField`` in a model takes a few steps: of ``MEDIA_ROOT`` it should upload files. #. All that will be stored in your database is a path to the file - (relative to ``MEDIA_ROOT``). You'll must likely want to use the + (relative to ``MEDIA_ROOT``). You'll most likely want to use the convenience ``get_<fieldname>_url`` function provided by Django. For example, if your ``ImageField`` is called ``mug_shot``, you can get the absolute URL to your image in a template with diff --git a/docs/fastcgi.txt b/docs/fastcgi.txt index 78ee9d408c..edd4c8a83d 100644 --- a/docs/fastcgi.txt +++ b/docs/fastcgi.txt @@ -79,9 +79,9 @@ your ``manage.py`` is), and then run ``manage.py`` with the ``runfcgi`` option:: If you specify ``help`` as the only option after ``runfcgi``, it'll display a list of all the available options. -You'll need to specify either a ``socket``, ``protocol`` or both ``host`` and ``port``. -Then, when you set up your Web server, you'll just need to point it at the host/port -or socket you specified when starting the FastCGI server. +You'll need to specify either a ``socket``, ``protocol`` or both ``host`` and +``port``. Then, when you set up your Web server, you'll just need to point it +at the host/port or socket you specified when starting the FastCGI server. Protocols --------- @@ -209,6 +209,9 @@ This is probably the most common case, if you're using Django's admin site:: .. _mod_rewrite: http://httpd.apache.org/docs/2.0/mod/mod_rewrite.html +Django will automatically use the pre-rewrite version of the URL when +constructing URLs with the ``{% url %}`` template tag (and similar methods). + lighttpd setup ============== @@ -336,3 +339,30 @@ detailed above. .. _modpython: ../modpython/#serving-the-admin-files +Forcing the URL prefix to a particular value +============================================ + +Because many of these fastcgi-based solutions require rewriting the URL at +some point inside the webserver, the path information that Django sees may not +resemble the original URL that was passed in. This is a problem if the Django +application is being served from under a particular prefix and you want your +URLs from the ``{% url %}`` tag to look like the prefix, rather than the +rewritten version, which might contain, for example, ``mysite.fcgi``. + +Django makes a good attempt to work out what the real script name prefix +should be. In particular, if the webserver sets the ``SCRIPT_URL`` (specific +to Apache's mod_rewrite), or ``REDIRECT_URL`` (set by a few servers, including +Apache + mod_rewrite in some situations), Django will work out the original +prefix automatically. + +In the cases where Django cannot work out the prefix correctly and where you +want the original value to be used in URLs, you can set the +``FORCE_SCRIPT_NAME`` setting in your main ``settings`` file. This sets the +script name uniformly for every URL served via that settings file. Thus you'll +need to use different settings files if you want different sets of URLs to +have different script names in this case, but that is a rare situation. + +As an example of how to use it, if your Django configuration is serving all of +the URLs under ``'/'`` and you wanted to use this setting, you would set +``FORCE_SCRIPT_NAME = ''`` in your settings file. + diff --git a/docs/form_for_model.txt b/docs/form_for_model.txt index ddca9aae18..a394cd2c54 100644 --- a/docs/form_for_model.txt +++ b/docs/form_for_model.txt @@ -20,13 +20,13 @@ For this reason, Django provides a few helper functions that let you create a ``form_for_model()`` -------------------- -The method ``django.newforms.form_for_model()`` creates a form based on the +The method ``django.forms.form_for_model()`` creates a form based on the definition of a specific model. Pass it the model class, and it will return a ``Form`` class that contains a form field for each model field. For example:: - >>> from django.newforms import form_for_model + >>> from django.forms import form_for_model # Create the form class. >>> ArticleForm = form_for_model(Article) @@ -93,11 +93,11 @@ the full list of conversions: As you might expect, the ``ForeignKey`` and ``ManyToManyField`` model field types are special cases: - * ``ForeignKey`` is represented by ``django.newforms.ModelChoiceField``, + * ``ForeignKey`` is represented by ``django.forms.ModelChoiceField``, which is a ``ChoiceField`` whose choices are a model ``QuerySet``. * ``ManyToManyField`` is represented by - ``django.newforms.ModelMultipleChoiceField``, which is a + ``django.forms.ModelMultipleChoiceField``, which is a ``MultipleChoiceField`` whose choices are a model ``QuerySet``. In addition, each generated form field has attributes set as follows: @@ -228,7 +228,7 @@ Using an alternate base class ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you want to add custom methods to the form generated by -``form_for_model()``, write a class that extends ``django.newforms.BaseForm`` +``form_for_model()``, write a class that extends ``django.forms.BaseForm`` and contains your custom methods. Then, use the ``form`` argument to ``form_for_model()`` to tell it to use your custom form as its base class. For example:: @@ -412,8 +412,8 @@ 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: ../newforms/#simple-view-example -.. _`simple example view`: ../newforms/#simple-view-example +.. _contact form: ../forms/#simple-view-example +.. _`simple example view`: ../forms/#simple-view-example When should you use ``form_for_model()`` and ``form_for_instance()``? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/form_preview.txt b/docs/form_preview.txt index e03de36187..171174704c 100644 --- a/docs/form_preview.txt +++ b/docs/form_preview.txt @@ -13,7 +13,7 @@ Python class. Overview ========= -Given a ``django.newforms.Form`` subclass that you define, this application +Given a ``django.forms.Form`` subclass that you define, this application takes care of the following workflow: 1. Displays the form as HTML on a Web page. @@ -65,7 +65,7 @@ How to use ``FormPreview`` from myapp.preview import SomeModelFormPreview from myapp.models import SomeModel - from django import newforms as forms + from django import forms ...and add the following line to the appropriate model in your URLconf:: diff --git a/docs/form_wizard.txt b/docs/form_wizard.txt index cd9e58ded1..661127e5b0 100644 --- a/docs/form_wizard.txt +++ b/docs/form_wizard.txt @@ -17,7 +17,7 @@ etc. The term "wizard," in this context, is `explained on Wikipedia`_. .. _explained on Wikipedia: http://en.wikipedia.org/wiki/Wizard_%28software%29 -.. _forms: ../newforms/ +.. _forms: ../forms/ How it works ============ @@ -41,7 +41,7 @@ Usage This application handles as much machinery for you as possible. Generally, you just have to do these things: - 1. Define a number of ``django.newforms`` ``Form`` classes -- one per wizard + 1. Define a number of ``django.forms`` ``Form`` classes -- one per wizard page. 2. Create a ``FormWizard`` class that specifies what to do once all of your forms have been submitted and validated. This also lets you override some @@ -55,8 +55,8 @@ Defining ``Form`` classes ========================= The first step in creating a form wizard is to create the ``Form`` classes. -These should be standard ``django.newforms`` ``Form`` classes, covered in the -`newforms documentation`_. +These should be standard ``django.forms`` ``Form`` classes, covered in the +`forms documentation`_. These classes can live anywhere in your codebase, but convention is to put them in a file called ``forms.py`` in your application. @@ -65,7 +65,7 @@ For example, let's write a "contact form" wizard, where the first page's form collects the sender's e-mail address and subject, and the second page collects the message itself. Here's what the ``forms.py`` might look like:: - from django import newforms as forms + from django import forms class ContactForm1(forms.Form): subject = forms.CharField(max_length=100) @@ -78,7 +78,7 @@ the message itself. Here's what the ``forms.py`` might look like:: data between pages, you may not include a ``FileField`` in any form except the last one. -.. _newforms documentation: ../newforms/ +.. _forms documentation: ../forms/ Creating a ``FormWizard`` class =============================== @@ -94,7 +94,7 @@ which specifies what should happen when the data for *every* form is submitted and validated. This method is passed two arguments: * ``request`` -- an HttpRequest_ object - * ``form_list`` -- a list of ``django.newforms`` ``Form`` classes + * ``form_list`` -- a list of ``django.forms`` ``Form`` classes In this simplistic example, rather than perform any database operation, the method simply renders a template of the validated data:: @@ -209,7 +209,7 @@ Default implementation:: def prefix_for_step(self, step): return str(step) -.. _form prefix documentation: ../newforms/#prefixes-for-forms +.. _form prefix documentation: ../forms/#prefixes-for-forms ``render_hash_failure`` ~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/forms.txt b/docs/forms.txt index 18d322a8eb..547323d103 100644 --- a/docs/forms.txt +++ b/docs/forms.txt @@ -1,700 +1,2472 @@ -=============================== -Forms, fields, and manipulators -=============================== +================= +The forms library +================= -Forwards-compatibility note -=========================== - -The legacy forms/manipulators system described in this document is going to be -replaced in the next Django release. If you're starting from scratch, we -strongly encourage you not to waste your time learning this. Instead, learn and -use the django.newforms system, which we have begun to document in the -`newforms documentation`_. - -If you have legacy form/manipulator code, read the "Migration plan" section in -that document to understand how we're making the switch. - -.. _newforms documentation: ../newforms/ - -Introduction -============ - -Once you've got a chance to play with Django's admin interface, you'll probably -wonder if the fantastic form validation framework it uses is available to user -code. It is, and this document explains how the framework works. - -We'll take a top-down approach to examining Django's form validation framework, -because much of the time you won't need to use the lower-level APIs. Throughout -this document, we'll be working with the following model, a "place" object:: - - from django.db import models - - PLACE_TYPES = ( - (1, 'Bar'), - (2, 'Restaurant'), - (3, 'Movie Theater'), - (4, 'Secret Hideout'), - ) - - class Place(models.Model): - 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(max_length=5, blank=True) - place_type = models.IntegerField(choices=PLACE_TYPES) - - class Admin: - pass - - def __unicode__(self): - return self.name - -Defining the above class is enough to create an admin interface to a ``Place``, -but what if you want to allow public users to submit places? - -Automatic Manipulators -====================== - -The highest-level interface for object creation and modification is the -**automatic Manipulator** framework. An automatic manipulator is a utility -class tied to a given model that "knows" how to create or modify instances of -that model and how to validate data for the object. Automatic Manipulators come -in two flavors: ``AddManipulators`` and ``ChangeManipulators``. Functionally -they are quite similar, but the former knows how to create new instances of the -model, while the latter modifies existing instances. Both types of classes are -automatically created when you define a new class:: - - >>> from mysite.myapp.models import Place - >>> Place.AddManipulator - <class 'django.models.manipulators.AddManipulator'> - >>> Place.ChangeManipulator - <class 'django.models.manipulators.ChangeManipulator'> - -Using the ``AddManipulator`` ----------------------------- - -We'll start with the ``AddManipulator``. Here's a very simple view that takes -POSTed data from the browser and creates a new ``Place`` object:: - - from django.shortcuts import render_to_response - from django.http import Http404, HttpResponse, HttpResponseRedirect - from django import forms - from mysite.myapp.models import Place - - def naive_create_place(request): - """A naive approach to creating places; don't actually use this!""" - # Create the AddManipulator. - manipulator = Place.AddManipulator() - - # Make a copy of the POSTed data so that do_html2python can - # modify it in place (request.POST is immutable). - new_data = request.POST.copy() - - # Convert the request data (which will all be strings) into the - # appropriate Python types for those fields. - manipulator.do_html2python(new_data) +``django.forms`` is Django's form-handling library. - # Save the new object. - new_place = manipulator.save(new_data) +.. admonition:: Looking for oldforms? - # It worked! - return HttpResponse("Place created: %s" % new_place) + ``django.forms`` was once called ``newforms`` since it replaced Django's + original form/manipulator/validation framework. The old form handling + library is still available as `django.oldforms`_, but will be removed + in a future version of Django. -The ``naive_create_place`` example works, but as you probably can tell, this -view has a number of problems: +.. _django.oldforms: ../oldforms/ - * No validation of any sort is performed. If, for example, the ``name`` field - isn't given in ``request.POST``, the save step will cause a database error - because that field is required. Ugly. +Overview +======== - * Even if you *do* perform validation, there's still no way to give that - information to the user in any sort of useful way. +``django.forms`` is intended to handle HTML form display, data processing +(validation) and redisplay. It's what you use if you want to perform +server-side validation for an HTML form. - * You'll have to separately create a form (and view) that submits to this - page, which is a pain and is redundant. +For example, if your Web site has a contact form that visitors can use to +send you e-mail, you'd use this library to implement the display of the HTML +form fields, along with the form validation. Any time you need to use an HTML +``<form>``, you can use this library. -Let's dodge these problems momentarily to take a look at how you could create a -view with a form that submits to this flawed creation view:: +The library deals with these concepts: - def naive_create_place_form(request): - """Simplistic place form view; don't actually use anything like this!""" - # Create a FormWrapper object that the template can use. Ignore - # the last two arguments to FormWrapper for now. - form = forms.FormWrapper(Place.AddManipulator(), {}, {}) - return render_to_response('places/naive_create_form.html', {'form': form}) + * **Widget** -- A class that corresponds to an HTML form widget, e.g. + ``<input type="text">`` or ``<textarea>``. This handles rendering of the + widget as HTML. -(This view, as well as all the following ones, has the same imports as in the -first example above.) + * **Field** -- A class that is responsible for doing validation, e.g. + an ``EmailField`` that makes sure its data is a valid e-mail address. -The ``forms.FormWrapper`` object is a wrapper that templates can -easily deal with to create forms. Here's the ``naive_create_form.html`` -template:: + * **Form** -- A collection of fields that knows how to validate itself and + display itself as HTML. - {% extends "base.html" %} + * **Media** -- A definition of the CSS and JavaScript resources that are + required to render a form. - {% block content %} - <h1>Create a place:</h1> +The library is decoupled from the other Django components, such as the database +layer, views and templates. It relies only on Django settings, a couple of +``django.utils`` helper functions and Django's internationalization hooks (but +you're not required to be using internationalization features to use this +library). - <form method="post" action="../do_new/"> - <p><label for="id_name">Name:</label> {{ form.name }}</p> - <p><label for="id_address">Address:</label> {{ form.address }}</p> - <p><label for="id_city">City:</label> {{ form.city }}</p> - <p><label for="id_state">State:</label> {{ form.state }}</p> - <p><label for="id_zip_code">Zip:</label> {{ form.zip_code }}</p> - <p><label for="id_place_type">Place type:</label> {{ form.place_type }}</p> - <input type="submit" /> - </form> - {% endblock %} +Form objects +============ -Before we get back to the problems with these naive set of views, let's go over -some salient points of the above template: +The primary way of using the ``forms`` library is to create a form object. +Do this by subclassing ``django.forms.Form`` and specifying the form's +fields, in a declarative style that you'll be familiar with if you've used +Django database models. In this section, we'll iteratively develop a form +object that you might use to implement "contact me" functionality on your +personal Web site. - * Field "widgets" are handled for you: ``{{ form.field }}`` automatically - creates the "right" type of widget for the form, as you can see with the - ``place_type`` field above. +Start with this basic ``Form`` subclass, which we'll call ``ContactForm``:: - * There isn't a way just to spit out the form. You'll still need to define - how the form gets laid out. This is a feature: Every form should be - designed differently. Django doesn't force you into any type of mold. - If you must use tables, use tables. If you're a semantic purist, you can - probably find better HTML than in the above template. + from django import forms - * To avoid name conflicts, the ``id`` values of form elements take the - form "id_*fieldname*". + class ContactForm(forms.Form): + subject = forms.CharField(max_length=100) + message = forms.CharField() + sender = forms.EmailField() + cc_myself = forms.BooleanField(required=False) -By creating a creation form we've solved problem number 3 above, but we still -don't have any validation. Let's revise the validation issue by writing a new -creation view that takes validation into account:: +A form is composed of ``Field`` objects. In this case, our form has four +fields: ``subject``, ``message``, ``sender`` and ``cc_myself``. We'll explain +the different types of fields -- e.g., ``CharField`` and ``EmailField`` -- +shortly. - def create_place_with_validation(request): - manipulator = Place.AddManipulator() - new_data = request.POST.copy() +Creating ``Form`` instances +--------------------------- - # Check for validation errors - errors = manipulator.get_validation_errors(new_data) - manipulator.do_html2python(new_data) - if errors: - return render_to_response('places/errors.html', {'errors': errors}) - else: - new_place = manipulator.save(new_data) - return HttpResponse("Place created: %s" % new_place) +A ``Form`` instance is either **bound** to a set of data, or **unbound**. -In this new version, errors will be found -- ``manipulator.get_validation_errors`` -handles all the validation for you -- and those errors can be nicely presented -on an error page (templated, of course):: + * If it's **bound** to a set of data, it's capable of validating that data + and rendering the form as HTML with the data displayed in the HTML. - {% extends "base.html" %} + * If it's **unbound**, it cannot do validation (because there's no data to + validate!), but it can still render the blank form as HTML. - {% block content %} +To create an unbound ``Form`` instance, simply instantiate the class:: - <h1>Please go back and correct the following error{{ errors|pluralize }}:</h1> - <ul> - {% for e in errors.items %} - <li>Field "{{ e.0 }}": {{ e.1|join:", " }}</li> - {% endfor %} - </ul> + >>> f = ContactForm() - {% endblock %} +To bind data to a form, pass the data as a dictionary as the first parameter to +your ``Form`` class constructor:: -Still, this has its own problems: + >>> data = {'subject': 'hello', + ... 'message': 'Hi there', + ... 'sender': 'foo@example.com', + ... 'cc_myself': True} + >>> f = ContactForm(data) - * There's still the issue of creating a separate (redundant) view for the - submission form. +In this dictionary, the keys are the field names, which correspond to the +attributes in your ``Form`` class. The values are the data you're trying +to validate. These will usually be strings, but there's no requirement that +they be strings; the type of data you pass depends on the ``Field``, as we'll +see in a moment. - * Errors, though nicely presented, are on a separate page, so the user will - have to use the "back" button to fix errors. That's ridiculous and unusable. +If you need to distinguish between bound and unbound form instances at runtime, +check the value of the form's ``is_bound`` attribute:: -The best way to deal with these issues is to collapse the two views -- the form -and the submission -- into a single view. This view will be responsible for -creating the form, validating POSTed data, and creating the new object (if the -data is valid). An added bonus of this approach is that errors and the form will -both be available on the same page, so errors with fields can be presented in -context. + >>> f = ContactForm() + >>> f.is_bound + False + >>> f = ContactForm({'subject': 'hello'}) + >>> f.is_bound + True -.. admonition:: Philosophy: +Note that passing an empty dictionary creates a *bound* form with empty data:: - Finally, for the HTTP purists in the audience (and the authorship), this - nicely matches the "true" meanings of HTTP GET and HTTP POST: GET fetches - the form, and POST creates the new object. + >>> f = ContactForm({}) + >>> f.is_bound + True -Below is the finished view:: +If you have a bound ``Form`` instance and want to change the data somehow, or +if you want to bind an unbound ``Form`` instance to some data, create another +``Form`` instance. There is no way to change data in a ``Form`` instance. Once +a ``Form`` instance has been created, you should consider its data immutable, +whether it has data or not. - def create_place(request): - manipulator = Place.AddManipulator() +Using forms to validate data +---------------------------- +The primary task of a ``Form`` object is to validate data. With a bound +``Form`` instance, call the ``is_valid()`` method to run validation and return +a boolean designating whether the data was valid:: + + >>> data = {'subject': 'hello', + ... 'message': 'Hi there', + ... 'sender': 'foo@example.com', + ... 'cc_myself': True} + >>> f = ContactForm(data) + >>> f.is_valid() + True + +Let's try with some invalid data. In this case, ``subject`` is blank (an error, +because all fields are required by default) and ``sender`` is not a valid +e-mail address:: + + >>> data = {'subject': '', + ... 'message': 'Hi there', + ... 'sender': 'invalid e-mail address', + ... 'cc_myself': True} + >>> f = ContactForm(data) + >>> f.is_valid() + False + +Access the ``errors`` attribute to get a dictionary of error messages:: + + >>> f.errors + {'sender': [u'Enter a valid e-mail address.'], 'subject': [u'This field is required.']} + +In this dictionary, the keys are the field names, and the values are lists of +Unicode strings representing the error messages. The error messages are stored +in lists because a field can have multiple error messages. + +You can access ``errors`` without having to call ``is_valid()`` first. The +form's data will be validated the first time either you call ``is_valid()`` or +access ``errors``. + +The validation routines will only get called once, regardless of how many times +you access ``errors`` or call ``is_valid()``. This means that if validation has +side effects, those side effects will only be triggered once. + +Behavior of unbound forms +~~~~~~~~~~~~~~~~~~~~~~~~~ + +It's meaningless to validate a form with no data, but, for the record, here's +what happens with unbound forms:: + + >>> f = ContactForm() + >>> f.is_valid() + False + >>> f.errors + {} + +Accessing "clean" data +---------------------- + +Each ``Field`` in a ``Form`` class is responsible not only for validating data, +but also for "cleaning" it -- normalizing it to a consistent format. This is a +nice feature, because it allows data for a particular field to be input in +a variety of ways, always resulting in consistent output. + +For example, ``DateField`` normalizes input into a Python ``datetime.date`` +object. Regardless of whether you pass it a string in the format +``'1994-07-15'``, a ``datetime.date`` object or a number of other formats, +``DateField`` will always normalize it to a ``datetime.date`` object as long as +it's valid. + +Once you've created a ``Form`` instance with a set of data and validated it, +you can access the clean data via the ``cleaned_data`` attribute of the ``Form`` +object:: + + >>> data = {'subject': 'hello', + ... 'message': 'Hi there', + ... 'sender': 'foo@example.com', + ... 'cc_myself': True} + >>> f = ContactForm(data) + >>> f.is_valid() + True + >>> f.cleaned_data + {'cc_myself': True, 'message': u'Hi there', 'sender': u'foo@example.com', 'subject': u'hello'} + +.. note:: + **New in Django development version** The ``cleaned_data`` attribute was + called ``clean_data`` in earlier releases. + +Note that any text-based field -- such as ``CharField`` or ``EmailField`` -- +always cleans the input into a Unicode string. We'll cover the encoding +implications later in this document. + +If your data does *not* validate, your ``Form`` instance will not have a +``cleaned_data`` attribute:: + + >>> data = {'subject': '', + ... 'message': 'Hi there', + ... 'sender': 'invalid e-mail address', + ... 'cc_myself': True} + >>> f = ContactForm(data) + >>> f.is_valid() + False + >>> f.cleaned_data + Traceback (most recent call last): + ... + AttributeError: 'ContactForm' object has no attribute 'cleaned_data' + +``cleaned_data`` will always *only* contain a key for fields defined in the +``Form``, even if you pass extra data when you define the ``Form``. In this +example, we pass a bunch of extra fields to the ``ContactForm`` constructor, +but ``cleaned_data`` contains only the form's fields:: + + >>> data = {'subject': 'hello', + ... 'message': 'Hi there', + ... 'sender': 'foo@example.com', + ... 'cc_myself': True, + ... 'extra_field_1': 'foo', + ... 'extra_field_2': 'bar', + ... 'extra_field_3': 'baz'} + >>> f = ContactForm(data) + >>> f.is_valid() + True + >>> f.cleaned_data # Doesn't contain extra_field_1, etc. + {'cc_myself': True, 'message': u'Hi there', 'sender': u'foo@example.com', 'subject': u'hello'} + +``cleaned_data`` will include a key and value for *all* fields defined in the +``Form``, even if the data didn't include a value for fields that are not +required. In this example, the data dictionary doesn't include a value for the +``nick_name`` field, but ``cleaned_data`` includes it, with an empty value:: + + >>> class OptionalPersonForm(Form): + ... first_name = CharField() + ... last_name = CharField() + ... nick_name = CharField(required=False) + >>> data = {'first_name': u'John', 'last_name': u'Lennon'} + >>> f = OptionalPersonForm(data) + >>> f.is_valid() + True + >>> f.cleaned_data + {'nick_name': u'', 'first_name': u'John', 'last_name': u'Lennon'} + +In this above example, the ``cleaned_data`` value for ``nick_name`` is set to an +empty string, because ``nick_name`` is ``CharField``, and ``CharField``\s treat +empty values as an empty string. Each field type knows what its "blank" value +is -- e.g., for ``DateField``, it's ``None`` instead of the empty string. For +full details on each field's behavior in this case, see the "Empty value" note +for each field in the "Built-in ``Field`` classes" section below. + +You can write code to perform validation for particular form fields (based on +their name) or for the form as a whole (considering combinations of various +fields). More information about this is in the `Custom form and field +validation`_ section, below. + +Behavior of unbound forms +~~~~~~~~~~~~~~~~~~~~~~~~~ + +It's meaningless to request "cleaned" data in a form with no data, but, for the +record, here's what happens with unbound forms:: + + >>> f = ContactForm() + >>> f.cleaned_data + Traceback (most recent call last): + ... + AttributeError: 'ContactForm' object has no attribute 'cleaned_data' + +Outputting forms as HTML +------------------------ + +The second task of a ``Form`` object is to render itself as HTML. To do so, +simply ``print`` it:: + + >>> f = ContactForm() + >>> print f + <tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" /></td></tr> + <tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" /></td></tr> + <tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" /></td></tr> + <tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" /></td></tr> + +If the form is bound to data, the HTML output will include that data +appropriately. For example, if a field is represented by an +``<input type="text">``, the data will be in the ``value`` attribute. If a +field is represented by an ``<input type="checkbox">``, then that HTML will +include ``checked="checked"`` if appropriate:: + + >>> data = {'subject': 'hello', + ... 'message': 'Hi there', + ... 'sender': 'foo@example.com', + ... 'cc_myself': True} + >>> f = ContactForm(data) + >>> print f + <tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" value="hello" /></td></tr> + <tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" value="Hi there" /></td></tr> + <tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" value="foo@example.com" /></td></tr> + <tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" checked="checked" /></td></tr> + +This default output is a two-column HTML table, with a ``<tr>`` for each field. +Notice the following: + + * For flexibility, the output does *not* include the ``<table>`` and + ``</table>`` tags, nor does it include the ``<form>`` and ``</form>`` + tags or an ``<input type="submit">`` tag. It's your job to do that. + + * Each field type has a default HTML representation. ``CharField`` and + ``EmailField`` are represented by an ``<input type="text">``. + ``BooleanField`` is represented by an ``<input type="checkbox">``. Note + these are merely sensible defaults; you can specify which HTML to use for + a given field by using widgets, which we'll explain shortly. + + * The HTML ``name`` for each tag is taken directly from its attribute name + in the ``ContactForm`` class. + + * The text label for each field -- e.g. ``'Subject:'``, ``'Message:'`` and + ``'Cc myself:'`` is generated from the field name by converting all + underscores to spaces and upper-casing the first letter. Again, note + these are merely sensible defaults; you can also specify labels manually. + + * Each text label is surrounded in an HTML ``<label>`` tag, which points + to the appropriate form field via its ``id``. Its ``id``, in turn, is + generated by prepending ``'id_'`` to the field name. The ``id`` + attributes and ``<label>`` tags are included in the output by default, to + follow best practices, but you can change that behavior. + +Although ``<table>`` output is the default output style when you ``print`` a +form, other output styles are available. Each style is available as a method on +a form object, and each rendering method returns a Unicode object. + +``as_p()`` +~~~~~~~~~~ + +``Form.as_p()`` renders the form as a series of ``<p>`` tags, with each ``<p>`` +containing one field:: + + >>> f = ContactForm() + >>> f.as_p() + u'<p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" /></p>\n<p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></p>\n<p><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" /></p>\n<p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></p>' + >>> print f.as_p() + <p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" /></p> + <p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></p> + <p><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" /></p> + <p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></p> + +``as_ul()`` +~~~~~~~~~~~ + +``Form.as_ul()`` renders the form as a series of ``<li>`` tags, with each +``<li>`` containing one field. It does *not* include the ``<ul>`` or ``</ul>``, +so that you can specify any HTML attributes on the ``<ul>`` for flexibility:: + + >>> f = ContactForm() + >>> f.as_ul() + u'<li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" /></li>\n<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></li>\n<li><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" /></li>\n<li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></li>' + >>> print f.as_ul() + <li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" /></li> + <li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></li> + <li><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" /></li> + <li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></li> + +``as_table()`` +~~~~~~~~~~~~~~ + +Finally, ``Form.as_table()`` outputs the form as an HTML ``<table>``. This is +exactly the same as ``print``. In fact, when you ``print`` a form object, it +calls its ``as_table()`` method behind the scenes:: + + >>> f = ContactForm() + >>> f.as_table() + u'<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" /></td></tr>\n<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" /></td></tr>\n<tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" /></td></tr>\n<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" /></td></tr>' + >>> print f.as_table() + <tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" /></td></tr> + <tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" /></td></tr> + <tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" /></td></tr> + <tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" /></td></tr> + +Configuring HTML ``<label>`` tags +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +An HTML ``<label>`` tag designates which label text is associated with which +form element. This small enhancement makes forms more usable and more accessible +to assistive devices. It's always a good idea to use ``<label>`` tags. + +By default, the form rendering methods include HTML ``id`` attributes on the +form elements and corresponding ``<label>`` tags around the labels. The ``id`` +attribute values are generated by prepending ``id_`` to the form field names. +This behavior is configurable, though, if you want to change the ``id`` +convention or remove HTML ``id`` attributes and ``<label>`` tags entirely. + +Use the ``auto_id`` argument to the ``Form`` constructor to control the label +and ``id`` behavior. This argument must be ``True``, ``False`` or a string. + +If ``auto_id`` is ``False``, then the form output will not include ``<label>`` +tags nor ``id`` attributes:: + + >>> f = ContactForm(auto_id=False) + >>> print f.as_table() + <tr><th>Subject:</th><td><input type="text" name="subject" maxlength="100" /></td></tr> + <tr><th>Message:</th><td><input type="text" name="message" /></td></tr> + <tr><th>Sender:</th><td><input type="text" name="sender" /></td></tr> + <tr><th>Cc myself:</th><td><input type="checkbox" name="cc_myself" /></td></tr> + >>> print f.as_ul() + <li>Subject: <input type="text" name="subject" maxlength="100" /></li> + <li>Message: <input type="text" name="message" /></li> + <li>Sender: <input type="text" name="sender" /></li> + <li>Cc myself: <input type="checkbox" name="cc_myself" /></li> + >>> print f.as_p() + <p>Subject: <input type="text" name="subject" maxlength="100" /></p> + <p>Message: <input type="text" name="message" /></p> + <p>Sender: <input type="text" name="sender" /></p> + <p>Cc myself: <input type="checkbox" name="cc_myself" /></p> + +If ``auto_id`` is set to ``True``, then the form output *will* include +``<label>`` tags and will simply use the field name as its ``id`` for each form +field:: + + >>> f = ContactForm(auto_id=True) + >>> print f.as_table() + <tr><th><label for="subject">Subject:</label></th><td><input id="subject" type="text" name="subject" maxlength="100" /></td></tr> + <tr><th><label for="message">Message:</label></th><td><input type="text" name="message" id="message" /></td></tr> + <tr><th><label for="sender">Sender:</label></th><td><input type="text" name="sender" id="sender" /></td></tr> + <tr><th><label for="cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="cc_myself" /></td></tr> + >>> print f.as_ul() + <li><label for="subject">Subject:</label> <input id="subject" type="text" name="subject" maxlength="100" /></li> + <li><label for="message">Message:</label> <input type="text" name="message" id="message" /></li> + <li><label for="sender">Sender:</label> <input type="text" name="sender" id="sender" /></li> + <li><label for="cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="cc_myself" /></li> + >>> print f.as_p() + <p><label for="subject">Subject:</label> <input id="subject" type="text" name="subject" maxlength="100" /></p> + <p><label for="message">Message:</label> <input type="text" name="message" id="message" /></p> + <p><label for="sender">Sender:</label> <input type="text" name="sender" id="sender" /></p> + <p><label for="cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="cc_myself" /></p> + +If ``auto_id`` is set to a string containing the format character ``'%s'``, +then the form output will include ``<label>`` tags, and will generate ``id`` +attributes based on the format string. For example, for a format string +``'field_%s'``, a field named ``subject`` will get the ``id`` value +``'field_subject'``. Continuing our example:: + + >>> f = ContactForm(auto_id='id_for_%s') + >>> print f.as_table() + <tr><th><label for="id_for_subject">Subject:</label></th><td><input id="id_for_subject" type="text" name="subject" maxlength="100" /></td></tr> + <tr><th><label for="id_for_message">Message:</label></th><td><input type="text" name="message" id="id_for_message" /></td></tr> + <tr><th><label for="id_for_sender">Sender:</label></th><td><input type="text" name="sender" id="id_for_sender" /></td></tr> + <tr><th><label for="id_for_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></td></tr> + >>> print f.as_ul() + <li><label for="id_for_subject">Subject:</label> <input id="id_for_subject" type="text" name="subject" maxlength="100" /></li> + <li><label for="id_for_message">Message:</label> <input type="text" name="message" id="id_for_message" /></li> + <li><label for="id_for_sender">Sender:</label> <input type="text" name="sender" id="id_for_sender" /></li> + <li><label for="id_for_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></li> + >>> print f.as_p() + <p><label for="id_for_subject">Subject:</label> <input id="id_for_subject" type="text" name="subject" maxlength="100" /></p> + <p><label for="id_for_message">Message:</label> <input type="text" name="message" id="id_for_message" /></p> + <p><label for="id_for_sender">Sender:</label> <input type="text" name="sender" id="id_for_sender" /></p> + <p><label for="id_for_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></p> + +If ``auto_id`` is set to any other true value -- such as a string that doesn't +include ``%s`` -- then the library will act as if ``auto_id`` is ``True``. + +By default, ``auto_id`` is set to the string ``'id_%s'``. + +Normally, a colon (``:``) will be appended after any label name when a form is +rendered. It's possible to change the colon to another character, or omit it +entirely, using the ``label_suffix`` parameter:: + + >>> f = ContactForm(auto_id='id_for_%s', label_suffix='') + >>> print f.as_ul() + <li><label for="id_for_subject">Subject</label> <input id="id_for_subject" type="text" name="subject" maxlength="100" /></li> + <li><label for="id_for_message">Message</label> <input type="text" name="message" id="id_for_message" /></li> + <li><label for="id_for_sender">Sender</label> <input type="text" name="sender" id="id_for_sender" /></li> + <li><label for="id_for_cc_myself">Cc myself</label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></li> + >>> f = ContactForm(auto_id='id_for_%s', label_suffix=' ->') + >>> print f.as_ul() + <li><label for="id_for_subject">Subject -></label> <input id="id_for_subject" type="text" name="subject" maxlength="100" /></li> + <li><label for="id_for_message">Message -></label> <input type="text" name="message" id="id_for_message" /></li> + <li><label for="id_for_sender">Sender -></label> <input type="text" name="sender" id="id_for_sender" /></li> + <li><label for="id_for_cc_myself">Cc myself -></label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></li> + +Note that the label suffix is added only if the last character of the +label isn't a punctuation character (``.``, ``!``, ``?`` or ``:``) + +Notes on field ordering +~~~~~~~~~~~~~~~~~~~~~~~ + +In the ``as_p()``, ``as_ul()`` and ``as_table()`` shortcuts, the fields are +displayed in the order in which you define them in your form class. For +example, in the ``ContactForm`` example, the fields are defined in the order +``subject``, ``message``, ``sender``, ``cc_myself``. To reorder the HTML +output, just change the order in which those fields are listed in the class. + +How errors are displayed +~~~~~~~~~~~~~~~~~~~~~~~~ + +If you render a bound ``Form`` object, the act of rendering will automatically +run the form's validation if it hasn't already happened, and the HTML output +will include the validation errors as a ``<ul class="errorlist">`` near the +field. The particular positioning of the error messages depends on the output +method you're using:: + + >>> data = {'subject': '', + ... 'message': 'Hi there', + ... 'sender': 'invalid e-mail address', + ... 'cc_myself': True} + >>> f = ContactForm(data, auto_id=False) + >>> print f.as_table() + <tr><th>Subject:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="subject" maxlength="100" /></td></tr> + <tr><th>Message:</th><td><input type="text" name="message" value="Hi there" /></td></tr> + <tr><th>Sender:</th><td><ul class="errorlist"><li>Enter a valid e-mail address.</li></ul><input type="text" name="sender" value="invalid e-mail address" /></td></tr> + <tr><th>Cc myself:</th><td><input checked="checked" type="checkbox" name="cc_myself" /></td></tr> + >>> print f.as_ul() + <li><ul class="errorlist"><li>This field is required.</li></ul>Subject: <input type="text" name="subject" maxlength="100" /></li> + <li>Message: <input type="text" name="message" value="Hi there" /></li> + <li><ul class="errorlist"><li>Enter a valid e-mail address.</li></ul>Sender: <input type="text" name="sender" value="invalid e-mail address" /></li> + <li>Cc myself: <input checked="checked" type="checkbox" name="cc_myself" /></li> + >>> print f.as_p() + <p><ul class="errorlist"><li>This field is required.</li></ul></p> + <p>Subject: <input type="text" name="subject" maxlength="100" /></p> + <p>Message: <input type="text" name="message" value="Hi there" /></p> + <p><ul class="errorlist"><li>Enter a valid e-mail address.</li></ul></p> + <p>Sender: <input type="text" name="sender" value="invalid e-mail address" /></p> + <p>Cc myself: <input checked="checked" type="checkbox" name="cc_myself" /></p> + +Customizing the error list format +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +By default, forms use ``django.forms.util.ErrorList`` to format validation +errors. If you'd like to use an alternate class for displaying errors, you can +pass that in at construction time:: + + >>> from django.forms.util import ErrorList + >>> class DivErrorList(ErrorList): + ... def __unicode__(self): + ... return self.as_divs() + ... def as_divs(self): + ... if not self: return u'' + ... return u'<div class="errorlist">%s</div>' % ''.join([u'<div class="error">%s</div>' % e for e in self]) + >>> f = ContactForm(data, auto_id=False, error_class=DivErrorList) + >>> f.as_p() + <div class="errorlist"><div class="error">This field is required.</div></div> + <p>Subject: <input type="text" name="subject" maxlength="100" /></p> + <p>Message: <input type="text" name="message" value="Hi there" /></p> + <div class="errorlist"><div class="error">Enter a valid e-mail address.</div></div> + <p>Sender: <input type="text" name="sender" value="invalid e-mail address" /></p> + <p>Cc myself: <input checked="checked" type="checkbox" name="cc_myself" /></p> + +More granular output +~~~~~~~~~~~~~~~~~~~~ + +The ``as_p()``, ``as_ul()`` and ``as_table()`` methods are simply shortcuts for +lazy developers -- they're not the only way a form object can be displayed. + +To display the HTML for a single field in your form, use dictionary lookup +syntax using the field's name as the key, and print the resulting object:: + + >>> f = ContactForm() + >>> print f['subject'] + <input id="id_subject" type="text" name="subject" maxlength="100" /> + >>> print f['message'] + <input type="text" name="message" id="id_message" /> + >>> print f['sender'] + <input type="text" name="sender" id="id_sender" /> + >>> print f['cc_myself'] + <input type="checkbox" name="cc_myself" id="id_cc_myself" /> + +Call ``str()`` or ``unicode()`` on the field to get its rendered HTML as a +string or Unicode object, respectively:: + + >>> str(f['subject']) + '<input id="id_subject" type="text" name="subject" maxlength="100" />' + >>> unicode(f['subject']) + u'<input id="id_subject" type="text" name="subject" maxlength="100" />' + +The field-specific output honors the form object's ``auto_id`` setting:: + + >>> f = ContactForm(auto_id=False) + >>> print f['message'] + <input type="text" name="message" /> + >>> f = ContactForm(auto_id='id_%s') + >>> print f['message'] + <input type="text" name="message" id="id_message" /> + +For a field's list of errors, access the field's ``errors`` attribute. This +is a list-like object that is displayed as an HTML ``<ul class="errorlist">`` +when printed:: + + >>> data = {'subject': 'hi', 'message': '', 'sender': '', 'cc_myself': ''} + >>> f = ContactForm(data, auto_id=False) + >>> print f['message'] + <input type="text" name="message" /> + >>> f['message'].errors + [u'This field is required.'] + >>> print f['message'].errors + <ul class="errorlist"><li>This field is required.</li></ul> + >>> f['subject'].errors + [] + >>> print f['subject'].errors + + >>> str(f['subject'].errors) + '' + +Using forms in views and templates +---------------------------------- + +Let's put this all together and use the ``ContactForm`` example in a Django +view and template. + +Simple view example +~~~~~~~~~~~~~~~~~~~ + +This example view displays the contact form by default and validates/processes +it if accessed via a POST request:: + + def contact(request): if request.method == 'POST': - # If data was POSTed, we're trying to create a new Place. - new_data = request.POST.copy() - - # Check for errors. - errors = manipulator.get_validation_errors(new_data) - manipulator.do_html2python(new_data) - - if not errors: - # No errors. This means we can save the data! - new_place = manipulator.save(new_data) - - # Redirect to the object's "edit" page. Always use a redirect - # after POST data, so that reloads don't accidently create - # duplicate entires, and so users don't see the confusing - # "Repost POST data?" alert box in their browsers. - return HttpResponseRedirect("/places/edit/%i/" % new_place.id) + form = ContactForm(request.POST) + if form.is_valid(): + # Do form processing here... + return HttpResponseRedirect('/url/on_success/') else: - # No POST, so we want a brand new form without any data or errors. - errors = new_data = {} - - # Create the FormWrapper, template, context, response. - form = forms.FormWrapper(manipulator, new_data, errors) - return render_to_response('places/create_form.html', {'form': form}) + form = ContactForm() + return render_to_response('contact.html', {'form': form}) -and here's the ``create_form`` template:: +Simple template example +~~~~~~~~~~~~~~~~~~~~~~~ - {% extends "base.html" %} +The template in the above view example, ``contact.html``, is responsible for +displaying the form as HTML. To do this, we can use the techniques outlined in +the "Outputting forms as HTML" section above. - {% block content %} - <h1>Create a place:</h1> - - {% if form.has_errors %} - <h2>Please correct the following error{{ form.error_dict|pluralize }}:</h2> - {% endif %} +The simplest way to display a form's HTML is to use the variable on its own, +like this:: - <form method="post" action="."> - <p> - <label for="id_name">Name:</label> {{ form.name }} - {% if form.name.errors %}*** {{ form.name.errors|join:", " }}{% endif %} - </p> - <p> - <label for="id_address">Address:</label> {{ form.address }} - {% if form.address.errors %}*** {{ form.address.errors|join:", " }}{% endif %} - </p> - <p> - <label for="id_city">City:</label> {{ form.city }} - {% if form.city.errors %}*** {{ form.city.errors|join:", " }}{% endif %} - </p> - <p> - <label for="id_state">State:</label> {{ form.state }} - {% if form.state.errors %}*** {{ form.state.errors|join:", " }}{% endif %} - </p> - <p> - <label for="id_zip_code">Zip:</label> {{ form.zip_code }} - {% if form.zip_code.errors %}*** {{ form.zip_code.errors|join:", " }}{% endif %} - </p> - <p> - <label for="id_place_type">Place type:</label> {{ form.place_type }} - {% if form.place_type.errors %}*** {{ form.place_type.errors|join:", " }}{% endif %} - </p> + <form method="post" action=""> + <table>{{ form }}</table> <input type="submit" /> </form> - {% endblock %} -The second two arguments to ``FormWrapper`` (``new_data`` and ``errors``) -deserve some mention. +The above template code will display the form as an HTML table, using the +``form.as_table()`` method explained previously. This works because Django's +template system displays an object's ``__str__()`` value, and the ``Form`` +class' ``__str__()`` method calls its ``as_table()`` method. -The first is any "default" data to be used as values for the fields. Pulling -the data from ``request.POST``, as is done above, makes sure that if there are -errors, the values the user put in aren't lost. If you try the above example, -you'll see this in action. +The following is equivalent but a bit more explicit:: -The second argument is the error list retrieved from -``manipulator.get_validation_errors``. When passed into the ``FormWrapper``, -this gives each field an ``errors`` item (which is a list of error messages -associated with the field) as well as a ``html_error_list`` item, which is a -``<ul>`` of error messages. The above template uses these error items to -display a simple error message next to each field. The error list is saved as -an ``error_dict`` attribute of the ``FormWrapper`` object. + <form method="post" action=""> + <table>{{ form.as_table }}</table> + <input type="submit" /> + </form> -Using the ``ChangeManipulator`` -------------------------------- +``form.as_ul`` and ``form.as_p`` are also available, as you may expect. -The above has covered using the ``AddManipulator`` to create a new object. What -about editing an existing one? It's shockingly similar to creating a new one:: +Note that in the above two examples, we included the ``<form>``, ``<table>`` +``<input type="submit" />``, ``</table>`` and ``</form>`` tags. The form +convenience methods (``as_table()``, ``as_ul()`` and ``as_p()``) do not include +that HTML. - def edit_place(request, place_id): - # Get the place in question from the database and create a - # ChangeManipulator at the same time. - try: - manipulator = Place.ChangeManipulator(place_id) - except Place.DoesNotExist: - raise Http404 +Complex template output +~~~~~~~~~~~~~~~~~~~~~~~ - # Grab the Place object in question for future use. - place = manipulator.original_object +As we've stressed several times, the ``as_table()``, ``as_ul()`` and ``as_p()`` +methods are just shortcuts for the common case. You can also work with the +individual fields for complete template control over the form's design. - if request.method == 'POST': - new_data = request.POST.copy() - errors = manipulator.get_validation_errors(new_data) - manipulator.do_html2python(new_data) - if not errors: - manipulator.save(new_data) - - # Do a post-after-redirect so that reload works, etc. - return HttpResponseRedirect("/places/edit/%i/" % place.id) - else: - errors = {} - # This makes sure the form accurate represents the fields of the place. - new_data = manipulator.flatten_data() - - form = forms.FormWrapper(manipulator, new_data, errors) - return render_to_response('places/edit_form.html', {'form': form, 'place': place}) +The easiest way is to iterate over the form's fields, with +``{% for field in form %}``. For example:: -The only real differences are: + <form method="post" action=""> + <dl> + {% for field in form %} + <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 %} + {% endfor %} + </dl> + <input type="submit" /> + </form> - * We create a ``ChangeManipulator`` instead of an ``AddManipulator``. - The argument to a ``ChangeManipulator`` is the ID of the object - to be changed. As you can see, the initializer will raise an - ``ObjectDoesNotExist`` exception if the ID is invalid. +This iteration technique is useful if you want to apply the same HTML +formatting to each field, or if you don't know the names of the form fields +ahead of time. Note that the fields will be iterated over in the order in which +they're defined in the ``Form`` class. - * ``ChangeManipulator.original_object`` stores the instance of the - object being edited. +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:: - * We set ``new_data`` based upon ``flatten_data()`` from the manipulator. - ``flatten_data()`` takes the data from the original object under - manipulation, and converts it into a data dictionary that can be used - to populate form elements with the existing values for the object. + <form method="post" action=""> + <ul class="myformclass"> + <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 %} - * The above example uses a different template, so create and edit can be - "skinned" differently if needed, but the form chunk itself is completely - identical to the one in the create form above. + <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 %} -The astute programmer will notice the add and create functions are nearly -identical and could in fact be collapsed into a single view. This is left as an -exercise for said programmer. + ... + </ul> + </form> -(However, the even-more-astute programmer will take heed of the note at the top -of this document and check out the `generic views`_ documentation if all she -wishes to do is this type of simple create/update.) +Highlighting required fields in templates +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It's common to show a user which fields are required. Here's an example of how +to do that, using the above example modified to insert an asterisk after the +label of each required field:: + + <form method="post" action=""> + <dl> + {% for field in form %} + <dt>{{ field.label_tag }}{% if field.field.required %}*{% endif %}</dt> + <dd>{{ field }}</dd> + {% if field.help_text %}<dd>{{ field.help_text }}</dd>{% endif %} + {% if field.errors %}<dd class="myerrors">{{ field.errors }}</dd>{% endif %} + {% endfor %} + </dl> + <input type="submit" /> + </form> -Custom forms and manipulators -============================= +The ``{% if field.field.required %}*{% endif %}`` fragment is the relevant +addition here. It adds the asterisk only if the field is required. -All the above is fine and dandy if you just want to use the automatically -created manipulators. But the coolness doesn't end there: You can easily create -your own custom manipulators for handling custom forms. +Note that we check ``field.field.required`` and not ``field.required``. In the +template, ``field`` is a ``forms.forms.BoundField`` instance, which holds +the actual ``Field`` instance in its ``field`` attribute. -Custom manipulators are pretty simple. Here's a manipulator that you might use -for a "contact" form on a website:: +Binding uploaded files to a form +-------------------------------- - from django import forms +**New in Django development version** - urgency_choices = ( - (1, "Extremely urgent"), - (2, "Urgent"), - (3, "Normal"), - (4, "Unimportant"), - ) - - class ContactManipulator(forms.Manipulator): - def __init__(self): - self.fields = ( - forms.EmailField(field_name="from", 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), - ) - -A certain similarity to Django's models should be apparent. The only required -method of a custom manipulator is ``__init__`` which must define the fields -present in the manipulator. See the ``django.forms`` module for -all the form fields provided by Django. - -You use this custom manipulator exactly as you would use an auto-generated one. -Here's a simple function that might drive the above form:: - - def contact_form(request): - manipulator = ContactManipulator() - if request.method == 'POST': - new_data = request.POST.copy() - errors = manipulator.get_validation_errors(new_data) - manipulator.do_html2python(new_data) - if not errors: +Dealing with forms that have ``FileField`` and ``ImageField`` fields +is a little more complicated than a normal form. - # Send e-mail using new_data here... +Firstly, in order to upload files, you'll need to make sure that your +``<form>`` element correctly defines the ``enctype`` as +``"multipart/form-data"``:: - return HttpResponseRedirect("/contact/thankyou/") - else: - errors = new_data = {} - form = forms.FormWrapper(manipulator, new_data, errors) - return render_to_response('contact_form.html', {'form': form}) + <form enctype="multipart/form-data" method="post" action="/foo/"> -Implementing ``flatten_data`` for custom manipulators ------------------------------------------------------- +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:: -It is possible (although rarely needed) to replace the default automatically -created manipulators on a model with your own custom manipulators. If you do -this and you are intending to use those models in generic views, you should -also define a ``flatten_data`` method in any ``ChangeManipulator`` replacement. -This should act like the default ``flatten_data`` and return a dictionary -mapping field names to their values, like so:: + # Bound form with an image field + >>> from django.core.files.uploadedfile import SimpleUploadedFile + >>> data = {'subject': 'hello', + ... 'message': 'Hi there', + ... 'sender': 'foo@example.com', + ... 'cc_myself': True} + >>> file_data = {'mugshot': SimpleUploadedFile('face.jpg', <file data>)} + >>> f = ContactFormWithMugshot(data, file_data) - def flatten_data(self): - obj = self.original_object - return dict( - from = obj.from, - subject = obj.subject, - ... - ) +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):: -In this way, your new change manipulator will act exactly like the default -version. + # Bound form with an image field, data from the request + >>> f = ContactFormWithMugshot(request.POST, request.FILES) -``FileField`` and ``ImageField`` special cases -============================================== +Constructing an unbound form is the same as always -- just omit both +form data *and* file data:: -Dealing with ``FileField`` and ``ImageField`` objects is a little more -complicated. + # Unbound form with a image field + >>> f = ContactFormWithMugshot() -First, you'll need to make sure that your ``<form>`` element correctly defines -the ``enctype`` as ``"multipart/form-data"``, in order to upload files:: +Testing for multipart forms +~~~~~~~~~~~~~~~~~~~~~~~~~~~ - <form enctype="multipart/form-data" method="post" action="/foo/"> +If you're writing reusable views or templates, you may not know ahead of time +whether your form is a multipart form or not. The ``is_multipart()`` method +tells you whether the form requires multipart encoding for submission:: -Next, you'll need to treat the field in the template slightly differently. A -``FileField`` or ``ImageField`` is represented by *two* HTML form elements. + >>> f = ContactFormWithMugshot() + >>> f.is_multipart() + True -For example, given this field in a model:: +Here's an example of how you might use this in a template:: - photo = model.ImageField('/path/to/upload/location') + {% if form.is_multipart %} + <form enctype="multipart/form-data" method="post" action="/foo/"> + {% else %} + <form method="post" action="/foo/"> + {% endif %} + {{ form }} + </form> -You'd need to display two formfields in the template:: +Subclassing forms +----------------- + +If you have multiple ``Form`` classes that share fields, you can use +subclassing to remove redundancy. + +When you subclass a custom ``Form`` class, the resulting subclass will +include all fields of the parent class(es), followed by the fields you define +in the subclass. + +In this example, ``ContactFormWithPriority`` contains all the fields from +``ContactForm``, plus an additional field, ``priority``. The ``ContactForm`` +fields are ordered first:: + + >>> class ContactFormWithPriority(ContactForm): + ... priority = forms.CharField() + >>> f = ContactFormWithPriority(auto_id=False) + >>> print f.as_ul() + <li>Subject: <input type="text" name="subject" maxlength="100" /></li> + <li>Message: <input type="text" name="message" /></li> + <li>Sender: <input type="text" name="sender" /></li> + <li>Cc myself: <input type="checkbox" name="cc_myself" /></li> + <li>Priority: <input type="text" name="priority" /></li> + +It's possible to subclass multiple forms, treating forms as "mix-ins." In this +example, ``BeatleForm`` subclasses both ``PersonForm`` and ``InstrumentForm`` +(in that order), and its field list includes the fields from the parent +classes:: + + >>> class PersonForm(Form): + ... first_name = CharField() + ... last_name = CharField() + >>> class InstrumentForm(Form): + ... instrument = CharField() + >>> class BeatleForm(PersonForm, InstrumentForm): + ... haircut_type = CharField() + >>> b = BeatleForm(auto_id=False) + >>> print b.as_ul() + <li>First name: <input type="text" name="first_name" /></li> + <li>Last name: <input type="text" name="last_name" /></li> + <li>Instrument: <input type="text" name="instrument" /></li> + <li>Haircut type: <input type="text" name="haircut_type" /></li> + +Prefixes for forms +------------------ + +You can put several Django forms inside one ``<form>`` tag. To give each +``Form`` its own namespace, use the ``prefix`` keyword argument:: + + >>> mother = PersonForm(prefix="mother") + >>> father = PersonForm(prefix="father") + >>> print mother.as_ul() + <li><label for="id_mother-first_name">First name:</label> <input type="text" name="mother-first_name" id="id_mother-first_name" /></li> + <li><label for="id_mother-last_name">Last name:</label> <input type="text" name="mother-last_name" id="id_mother-last_name" /></li> + >>> print father.as_ul() + <li><label for="id_father-first_name">First name:</label> <input type="text" name="father-first_name" id="id_father-first_name" /></li> + <li><label for="id_father-last_name">Last name:</label> <input type="text" name="father-last_name" id="id_father-last_name" /></li> + +Fields +====== + +When you create a ``Form`` class, the most important part is defining the +fields of the form. Each field has custom validation logic, along with a few +other hooks. + +Although the primary way you'll use ``Field`` classes is in ``Form`` classes, +you can also instantiate them and use them directly to get a better idea of +how they work. Each ``Field`` instance has a ``clean()`` method, which takes +a single argument and either raises a ``django.forms.ValidationError`` +exception or returns the clean value:: + + >>> f = forms.EmailField() + >>> f.clean('foo@example.com') + u'foo@example.com' + >>> f.clean(u'foo@example.com') + u'foo@example.com' + >>> f.clean('invalid e-mail address') + Traceback (most recent call last): + ... + ValidationError: [u'Enter a valid e-mail address.'] + +If you've used Django's old forms/validation framework, take care in noticing +this ``ValidationError`` is different than the previous ``ValidationError``. +This one lives at ``django.forms.ValidationError`` rather than +``django.core.validators.ValidationError``. + +Core field arguments +-------------------- + +Each ``Field`` class constructor takes at least these arguments. Some +``Field`` classes take additional, field-specific arguments, but the following +should *always* be accepted: + +``required`` +~~~~~~~~~~~~ + +By default, each ``Field`` class assumes the value is required, so if you pass +an empty value -- either ``None`` or the empty string (``""``) -- then +``clean()`` will raise a ``ValidationError`` exception:: + + >>> f = forms.CharField() + >>> f.clean('foo') + u'foo' + >>> f.clean('') + Traceback (most recent call last): + ... + ValidationError: [u'This field is required.'] + >>> f.clean(None) + Traceback (most recent call last): + ... + ValidationError: [u'This field is required.'] + >>> f.clean(' ') + u' ' + >>> f.clean(0) + u'0' + >>> f.clean(True) + u'True' + >>> f.clean(False) + u'False' + +To specify that a field is *not* required, pass ``required=False`` to the +``Field`` constructor:: + + >>> f = forms.CharField(required=False) + >>> f.clean('foo') + u'foo' + >>> f.clean('') + u'' + >>> f.clean(None) + u'' + >>> f.clean(0) + u'0' + >>> f.clean(True) + u'True' + >>> f.clean(False) + u'False' + +If a ``Field`` has ``required=False`` and you pass ``clean()`` an empty value, +then ``clean()`` will return a *normalized* empty value rather than raising +``ValidationError``. For ``CharField``, this will be a Unicode empty string. +For other ``Field`` classes, it might be ``None``. (This varies from field to +field.) + +``label`` +~~~~~~~~~ + +The ``label`` argument lets you specify the "human-friendly" label for this +field. This is used when the ``Field`` is displayed in a ``Form``. + +As explained in "Outputting forms as HTML" above, the default label for a +``Field`` is generated from the field name by converting all underscores to +spaces and upper-casing the first letter. Specify ``label`` if that default +behavior doesn't result in an adequate label. + +Here's a full example ``Form`` that implements ``label`` for two of its fields. +We've specified ``auto_id=False`` to simplify the output:: + + >>> class CommentForm(forms.Form): + ... name = forms.CharField(label='Your name') + ... url = forms.URLField(label='Your Web site', required=False) + ... comment = forms.CharField() + >>> f = CommentForm(auto_id=False) + >>> print f + <tr><th>Your name:</th><td><input type="text" name="name" /></td></tr> + <tr><th>Your Web site:</th><td><input type="text" name="url" /></td></tr> + <tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr> + +``initial`` +~~~~~~~~~~~ + +The ``initial`` argument lets you specify the initial value to use when +rendering this ``Field`` in an unbound ``Form``. + +The use-case for this is when you want to display an "empty" form in which a +field is initialized to a particular value. For example:: + + >>> class CommentForm(forms.Form): + ... name = forms.CharField(initial='Your name') + ... url = forms.URLField(initial='http://') + ... comment = forms.CharField() + >>> f = CommentForm(auto_id=False) + >>> print f + <tr><th>Name:</th><td><input type="text" name="name" value="Your name" /></td></tr> + <tr><th>Url:</th><td><input type="text" name="url" value="http://" /></td></tr> + <tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr> + +You may be thinking, why not just pass a dictionary of the initial values as +data when displaying the form? Well, if you do that, you'll trigger validation, +and the HTML output will include any validation errors:: + + >>> class CommentForm(forms.Form): + ... name = forms.CharField() + ... url = forms.URLField() + ... comment = forms.CharField() + >>> default_data = {'name': 'Your name', 'url': 'http://'} + >>> f = CommentForm(default_data, auto_id=False) + >>> print f + <tr><th>Name:</th><td><input type="text" name="name" value="Your name" /></td></tr> + <tr><th>Url:</th><td><ul class="errorlist"><li>Enter a valid URL.</li></ul><input type="text" name="url" value="http://" /></td></tr> + <tr><th>Comment:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="comment" /></td></tr> + +This is why ``initial`` values are only displayed for unbound forms. For bound +forms, the HTML output will use the bound data. + +Also note that ``initial`` values are *not* used as "fallback" data in +validation if a particular field's value is not given. ``initial`` values are +*only* intended for initial form display:: + + >>> class CommentForm(forms.Form): + ... name = forms.CharField(initial='Your name') + ... url = forms.URLField(initial='http://') + ... comment = forms.CharField() + >>> data = {'name': '', 'url': '', 'comment': 'Foo'} + >>> f = CommentForm(data) + >>> f.is_valid() + False + # The form does *not* fall back to using the initial values. + >>> f.errors + {'url': [u'This field is required.'], 'name': [u'This field is required.']} + +``widget`` +~~~~~~~~~~ + +The ``widget`` argument lets you specify a ``Widget`` class to use when +rendering this ``Field``. See `Widgets`_ below for more information. + +``help_text`` +~~~~~~~~~~~~~ + +The ``help_text`` argument lets you specify descriptive text for this +``Field``. If you provide ``help_text``, it will be displayed next to the +``Field`` when the ``Field`` is rendered by one of the convenience ``Form`` +methods (e.g., ``as_ul()``). + +Here's a full example ``Form`` that implements ``help_text`` for two of its +fields. We've specified ``auto_id=False`` to simplify the output:: + + >>> class HelpTextContactForm(forms.Form): + ... subject = forms.CharField(max_length=100, help_text='100 characters max.') + ... message = forms.CharField() + ... sender = forms.EmailField(help_text='A valid e-mail address, please.') + ... cc_myself = forms.BooleanField(required=False) + >>> f = HelpTextContactForm(auto_id=False) + >>> print f.as_table() + <tr><th>Subject:</th><td><input type="text" name="subject" maxlength="100" /><br />100 characters max.</td></tr> + <tr><th>Message:</th><td><input type="text" name="message" /></td></tr> + <tr><th>Sender:</th><td><input type="text" name="sender" /><br />A valid e-mail address, please.</td></tr> + <tr><th>Cc myself:</th><td><input type="checkbox" name="cc_myself" /></td></tr> + >>> print f.as_ul() + <li>Subject: <input type="text" name="subject" maxlength="100" /> 100 characters max.</li> + <li>Message: <input type="text" name="message" /></li> + <li>Sender: <input type="text" name="sender" /> A valid e-mail address, please.</li> + <li>Cc myself: <input type="checkbox" name="cc_myself" /></li> + >>> print f.as_p() + <p>Subject: <input type="text" name="subject" maxlength="100" /> 100 characters max.</p> + <p>Message: <input type="text" name="message" /></p> + <p>Sender: <input type="text" name="sender" /> A valid e-mail address, please.</p> + <p>Cc myself: <input type="checkbox" name="cc_myself" /></p> + +``error_messages`` +~~~~~~~~~~~~~~~~~~ + +**New in Django development version** + +The ``error_messages`` argument lets you override the default messages that the +field will raise. Pass in a dictionary with keys matching the error messages you +want to override. For example, here is the default error message:: + + >>> generic = forms.CharField() + >>> generic.clean('') + Traceback (most recent call last): + ... + ValidationError: [u'This field is required.'] + +And here is a custom error message:: + + >>> name = forms.CharField(error_messages={'required': 'Please enter your name'}) + >>> name.clean('') + Traceback (most recent call last): + ... + ValidationError: [u'Please enter your name'] + +In the `built-in Field classes`_ section below, each ``Field`` defines the +error message keys it uses. + +Dynamic initial values +---------------------- + +The ``initial`` argument to ``Field`` (explained above) lets you hard-code the +initial value for a ``Field`` -- but what if you want to declare the initial +value at runtime? For example, you might want to fill in a ``username`` field +with the username of the current session. + +To accomplish this, use the ``initial`` argument to a ``Form``. This argument, +if given, should be a dictionary mapping field names to initial values. Only +include the fields for which you're specifying an initial value; it's not +necessary to include every field in your form. For example:: + + >>> class CommentForm(forms.Form): + ... name = forms.CharField() + ... url = forms.URLField() + ... comment = forms.CharField() + >>> f = CommentForm(initial={'name': 'your username'}, auto_id=False) + >>> print f + <tr><th>Name:</th><td><input type="text" name="name" value="your username" /></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> + >>> f = CommentForm(initial={'name': 'another username'}, auto_id=False) + >>> print f + <tr><th>Name:</th><td><input type="text" name="name" value="another username" /></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> + +Just like the ``initial`` parameter to ``Field``, these values are only +displayed for unbound forms, and they're not used as fallback values if a +particular value isn't provided. + +Finally, note that if a ``Field`` defines ``initial`` *and* you include +``initial`` when instantiating the ``Form``, then the latter ``initial`` will +have precedence. In this example, ``initial`` is provided both at the field +level and at the form instance level, and the latter gets precedence:: + + >>> class CommentForm(forms.Form): + ... name = forms.CharField(initial='class') + ... url = forms.URLField() + ... comment = forms.CharField() + >>> f = CommentForm(initial={'name': 'instance'}, auto_id=False) + >>> print f + <tr><th>Name:</th><td><input type="text" name="name" value="instance" /></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> + +Built-in ``Field`` classes +-------------------------- + +Naturally, the ``forms`` library comes with a set of ``Field`` classes that +represent common validation needs. This section documents each built-in field. + +For each field, we describe the default widget used if you don't specify +``widget``. We also specify the value returned when you provide an empty value +(see the section on ``required`` above to understand what that means). + +``BooleanField`` +~~~~~~~~~~~~~~~~ + + * Default widget: ``CheckboxInput`` + * Empty value: ``False`` + * Normalizes to: A Python ``True`` or ``False`` value. + * Validates that the check box is checked (i.e. the value is ``True``) if + the field has ``required=True``. + * Error message keys: ``required`` + +**New in Django development version:** The empty value for a ``CheckboxInput`` +(and hence the standard ``BooleanField``) has changed to return ``False`` +instead of ``None`` in the development version. + +.. note:: + Since all ``Field`` subclasses have ``required=True`` by default, the + validation condition here is important. If you want to include a checkbox + in your form that can be either checked or unchecked, you must remember to + pass in ``required=False`` when creating the ``BooleanField``. + +``CharField`` +~~~~~~~~~~~~~ + + * Default widget: ``TextInput`` + * Empty value: ``''`` (an empty string) + * Normalizes to: A Unicode object. + * Validates ``max_length`` or ``min_length``, if they are provided. + Otherwise, all inputs are valid. + * Error message keys: ``required``, ``max_length``, ``min_length`` + +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. + +``ChoiceField`` +~~~~~~~~~~~~~~~ + + * Default widget: ``Select`` + * Empty value: ``''`` (an empty string) + * Normalizes to: A Unicode object. + * Validates that the given value exists in the list of choices. + * Error message keys: ``required``, ``invalid_choice`` + +Takes one extra argument, ``choices``, which is an iterable (e.g., a list or +tuple) of 2-tuples to use as choices for this field. This argument accepts +the same formats as the ``choices`` argument to a model field. See the +`model API documentation on choices`_ for more details. + +.. _model API documentation on choices: ../model-api#choices + +``DateField`` +~~~~~~~~~~~~~ + + * Default widget: ``TextInput`` + * Empty value: ``None`` + * Normalizes to: A Python ``datetime.date`` object. + * Validates that the given value is either a ``datetime.date``, + ``datetime.datetime`` or string formatted in a particular date format. + * Error message keys: ``required``, ``invalid`` + +Takes one optional argument, ``input_formats``, which is a list of formats used +to attempt to convert a string to a valid ``datetime.date`` object. + +If no ``input_formats`` argument is provided, the default input formats are:: + + '%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', # '2006-10-25', '10/25/2006', '10/25/06' + '%b %d %Y', '%b %d, %Y', # 'Oct 25 2006', 'Oct 25, 2006' + '%d %b %Y', '%d %b, %Y', # '25 Oct 2006', '25 Oct, 2006' + '%B %d %Y', '%B %d, %Y', # 'October 25 2006', 'October 25, 2006' + '%d %B %Y', '%d %B, %Y', # '25 October 2006', '25 October, 2006' + +``DateTimeField`` +~~~~~~~~~~~~~~~~~ + + * Default widget: ``DateTimeInput`` + * Empty value: ``None`` + * Normalizes to: A Python ``datetime.datetime`` object. + * Validates that the given value is either a ``datetime.datetime``, + ``datetime.date`` or string formatted in a particular datetime format. + * Error message keys: ``required``, ``invalid`` + +Takes one optional argument, ``input_formats``, which is a list of formats used +to attempt to convert a string to a valid ``datetime.datetime`` object. + +If no ``input_formats`` argument is provided, the default input formats are:: + + '%Y-%m-%d %H:%M:%S', # '2006-10-25 14:30:59' + '%Y-%m-%d %H:%M', # '2006-10-25 14:30' + '%Y-%m-%d', # '2006-10-25' + '%m/%d/%Y %H:%M:%S', # '10/25/2006 14:30:59' + '%m/%d/%Y %H:%M', # '10/25/2006 14:30' + '%m/%d/%Y', # '10/25/2006' + '%m/%d/%y %H:%M:%S', # '10/25/06 14:30:59' + '%m/%d/%y %H:%M', # '10/25/06 14:30' + '%m/%d/%y', # '10/25/06' + +**New in Django development version:** The ``DateTimeField`` used to use a +``TextInput`` widget by default. This has now changed. + +``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. + * Error message keys: ``required``, ``invalid``, ``max_value``, + ``min_value``, ``max_digits``, ``max_decimal_places``, + ``max_whole_digits`` + +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`` +~~~~~~~~~~~~~~ + + * Default widget: ``TextInput`` + * Empty value: ``''`` (an empty string) + * Normalizes to: A Unicode object. + * Validates that the given value is a valid e-mail address, using a + moderately complex regular expression. + * Error message keys: ``required``, ``invalid`` + +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. + * Error message keys: ``required``, ``invalid``, ``missing``, ``empty`` + +To learn more about the ``UploadedFile`` object, see the `file uploads documentation`_. + +When you use a ``FileField`` in a form, you must also remember to +`bind the file data to the form`_. + +.. _file uploads documentation: ../upload_handling/ +.. _`bind the file data to the form`: `Binding uploaded files to a form`_ + +``FilePathField`` +~~~~~~~~~~~~~~~~~ + +**New in Django development version** + + * Default widget: ``Select`` + * Empty value: ``None`` + * Normalizes to: A unicode object + * Validates that the selected choice exists in the list of choices. + * Error message keys: ``required``, ``invalid_choice`` + +The field allows choosing from files inside a certain directory. It takes three +extra arguments: + + ============== ========== =============================================== + Argument Required? Description + ============== ========== =============================================== + ``path`` Yes The absolute path to the directory whose + contents you want listed. This directory must + exist. + + ``recursive`` No If ``False`` (the default) only the direct + contents of ``path`` will be offered as choices. + If ``True``, the directory will be descended + into recursively and all descendants will be + listed as choices. + + ``match`` No A regular expression pattern; only files with + names matching this expression will be allowed + as choices. + ============== ========== =============================================== + +``FloatField`` +~~~~~~~~~~~~~~ + + * Default widget: ``TextInput`` + * Empty value: ``None`` + * Normalizes to: A Python float. + * Validates that the given value is an float. Leading and trailing + whitespace is allowed, as in Python's ``float()`` function. + * Error message keys: ``required``, ``invalid``, ``max_value``, + ``min_value`` + +Takes two optional arguments for validation, ``max_value`` and ``min_value``. +These control the range of values permitted in the field. + +``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. + * Error message keys: ``required``, ``invalid``, ``missing``, ``empty``, + ``invalid_image`` + +Using an ImageField requires that the `Python Imaging Library`_ is installed. + +When you use an ``ImageField`` in a form, you must also remember to +`bind the file data to the form`_. + +.. _Python Imaging Library: http://www.pythonware.com/products/pil/ + +``IntegerField`` +~~~~~~~~~~~~~~~~ + + * Default widget: ``TextInput`` + * Empty value: ``None`` + * Normalizes to: A Python integer or long integer. + * Validates that the given value is an integer. Leading and trailing + whitespace is allowed, as in Python's ``int()`` function. + * Error message keys: ``required``, ``invalid``, ``max_value``, + ``min_value`` + +Takes two optional arguments for validation, ``max_value`` and ``min_value``. +These control the range of values permitted in the field. + +``IPAddressField`` +~~~~~~~~~~~~~~~~~~ + + * Default widget: ``TextInput`` + * Empty value: ``''`` (an empty string) + * Normalizes to: A Unicode object. + * Validates that the given value is a valid IPv4 address, using a regular + expression. + * Error message keys: ``required``, ``invalid`` + +``MultipleChoiceField`` +~~~~~~~~~~~~~~~~~~~~~~~ + + * Default widget: ``SelectMultiple`` + * Empty value: ``[]`` (an empty list) + * Normalizes to: A list of Unicode objects. + * Validates that every value in the given list of values exists in the list + of choices. + * Error message keys: ``required``, ``invalid_choice``, ``invalid_list`` + +Takes one extra argument, ``choices``, which is an iterable (e.g., a list or +tuple) of 2-tuples to use as choices for this field. This argument accepts +the same formats as the ``choices`` argument to a model field. See the +`model API documentation on choices`_ for more details. + +``NullBooleanField`` +~~~~~~~~~~~~~~~~~~~~ + + * Default widget: ``NullBooleanSelect`` + * Empty value: ``None`` + * Normalizes to: A Python ``True``, ``False`` or ``None`` value. + * Validates nothing (i.e., it never raises a ``ValidationError``). + +``RegexField`` +~~~~~~~~~~~~~~ + + * Default widget: ``TextInput`` + * Empty value: ``''`` (an empty string) + * Normalizes to: A Unicode object. + * Validates that the given value matches against a certain regular + expression. + * Error message keys: ``required``, ``invalid`` + +Takes one required argument, ``regex``, which is a regular expression specified +either as a string or a compiled regular expression object. + +Also takes the following optional arguments: + + ====================== ===================================================== + Argument Description + ====================== ===================================================== + ``max_length`` Ensures the string has at most this many characters. + ``min_length`` Ensures the string has at least this many characters. + ====================== ===================================================== + +The optional argument ``error_message`` is also accepted for backwards +compatibility. The preferred way to provide an error message is to use the +``error_messages`` argument, passing a dictionary with ``'invalid'`` as a key +and the error message as the value. + +``TimeField`` +~~~~~~~~~~~~~ + + * Default widget: ``TextInput`` + * Empty value: ``None`` + * Normalizes to: A Python ``datetime.time`` object. + * Validates that the given value is either a ``datetime.time`` or string + formatted in a particular time format. + * Error message keys: ``required``, ``invalid`` + +Takes one optional argument, ``input_formats``, which is a list of formats used +to attempt to convert a string to a valid ``datetime.time`` object. + +If no ``input_formats`` argument is provided, the default input formats are:: + + '%H:%M:%S', # '14:30:59' + '%H:%M', # '14:30' + +``URLField`` +~~~~~~~~~~~~ + + * Default widget: ``TextInput`` + * Empty value: ``''`` (an empty string) + * Normalizes to: A Unicode object. + * Validates that the given value is a valid URL. + * Error message keys: ``required``, ``invalid``, ``invalid_link`` + +Takes the following optional arguments: + + ======================== ===================================================== + Argument Description + ======================== ===================================================== + ``max_length`` Ensures the string has at most this many characters. + ``min_length`` Ensures the string has at least this many characters. + ``verify_exists`` If ``True``, the validator will attempt to load the + given URL, raising ``ValidationError`` if the page + gives a 404. Defaults to ``False``. + ``validator_user_agent`` String used as the user-agent used when checking for + a URL's existence. Defaults to the value of the + ``URL_VALIDATOR_USER_AGENT`` setting. + ======================== ===================================================== + +Slightly complex built-in ``Field`` classes +------------------------------------------- + +The following are not yet documented here. See the unit tests, linked-to from +the bottom of this document, for examples of their use. + +``ComboField`` +~~~~~~~~~~~~~~ + +``MultiValueField`` +~~~~~~~~~~~~~~~~~~~ + +``SplitDateTimeField`` +~~~~~~~~~~~~~~~~~~~~~~ + +Fields which handle relationships +--------------------------------- + +For representing relationships between models, two fields are +provided which can derive their choices from a ``QuerySet``, and which +place one or more model objects into the ``cleaned_data`` dictionary +of forms in which they're used. Both of these fields have an +additional required argument: + +``queryset`` + A ``QuerySet`` of model objects from which the choices for the + field will be derived, and which will be used to validate the + user's selection. + +``ModelChoiceField`` +~~~~~~~~~~~~~~~~~~~~ + +Allows the selection of a single model object, suitable for +representing a foreign key. + +The ``__unicode__`` method of the model will be called to generate +string representations of the objects for use in the field's choices; +to provide customized representations, subclass ``ModelChoiceField`` +and override ``label_from_instance``. This method will receive a model +object, and should return a string suitable for representing it. For +example:: + + class MyModelChoiceField(ModelChoiceField): + def label_from_instance(self, obj): + return "My Object #%i" % obj.id + +``ModelMultipleChoiceField`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Allows the selection of one or more model objects, suitable for +representing a many-to-many relation. As with ``ModelChoiceField``, +you can use ``label_from_instance`` to customize the object +representations. + +Creating custom fields +---------------------- + +If the built-in ``Field`` classes don't meet your needs, you can easily create +custom ``Field`` classes. To do this, just create a subclass of +``django.forms.Field``. Its only requirements are that it implement a +``clean()`` method and that its ``__init__()`` method accept the core arguments +mentioned above (``required``, ``label``, ``initial``, ``widget``, +``help_text``). + +Custom form and field validation +--------------------------------- + +Form validation happens when the data is cleaned. If you want to customize +this process, there are various places you can change, each one serving a +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()`` +directly), but normally they won't be needed. + +In general, any cleaning method can raise ``ValidationError`` if there is a +problem with the data it is processing, passing the relevant error message to +the ``ValidationError`` constructor. If no ``ValidationError`` is raised, the +method should return the cleaned (normalized) data as a Python object. + +If you detect multiple errors during a cleaning method and wish to signal all +of them to the form submitter, it is possible to pass a list of errors to the +``ValidationError`` constructor. + +The three types of cleaning methods are: + + * The ``clean()`` method on a Field subclass. This is responsible + for cleaning the data in a way that is generic for that type of field. + For example, a FloatField will turn the data into a Python ``float`` or + raise a ``ValidationError``. + + * The ``clean_<fieldname>()`` method in a form subclass -- where + ``<fieldname>`` is replaced with the name of the form field attribute. + This method does any cleaning that is specific to that particular + attribute, unrelated to the type of field that it is. This method is not + passed any parameters. You will need to look up the value of the field + in ``self.cleaned_data`` and remember that it will be a Python object + at this point, not the original string submitted in the form (it will be + in ``cleaned_data`` because the general field ``clean()`` method, above, + has already cleaned the data once). + + For example, if you wanted to validate that the contents of a + ``CharField`` called ``serialnumber`` was unique, + ``clean_serialnumber()`` would be the right place to do this. You don't + need a specific field (it's just a ``CharField``), but you want a + formfield-specific piece of validation and, possibly, + cleaning/normalizing the data. + + * The Form subclass's ``clean()`` method. This method can perform + any validation that requires access to multiple fields from the form at + once. This is where you might put in things to check that if field ``A`` + is supplied, field ``B`` must contain a valid e-mail address and the + like. The data that this method returns is the final ``cleaned_data`` + attribute for the form, so don't forget to return the full list of + cleaned data if you override this method (by default, ``Form.clean()`` + just returns ``self.cleaned_data``). + + Note that any errors raised by your ``Form.clean()`` override will not + be associated with any field in particular. They go into a special + "field" (called ``__all__``), which you can access via the + ``non_field_errors()`` method if you need to. + +These methods are run in the order given above, one field at a time. That is, +for each field in the form (in the order they are declared in the form +definition), the ``Field.clean()`` method (or its override) is run, then +``clean_<fieldname>()``. Finally, once those two methods are run for every +field, the ``Form.clean()`` method, or its override, is executed. + +As mentioned above, any of these methods can raise a ``ValidationError``. For +any field, if the ``Field.clean()`` method raises a ``ValidationError``, any +field-specific cleaning method is not called. However, the cleaning methods +for all remaining fields are still executed. + +The ``clean()`` method for the ``Form`` class or subclass is always run. If +that method raises a ``ValidationError``, ``cleaned_data`` will be an empty +dictionary. + +The previous paragraph means that if you are overriding ``Form.clean()``, you +should iterate through ``self.cleaned_data.items()``, possibly considering the +``_errors`` dictionary attribute on the form as well. In this way, you will +already know which fields have passed their individual validation requirements. + +A simple example +~~~~~~~~~~~~~~~~ + +Here's a simple example of a custom field that validates its input is a string +containing comma-separated e-mail addresses, with at least one address. We'll +keep it simple and assume e-mail validation is contained in a function called +``is_valid_email()``. The full class:: - <p><label for="id_photo">Photo:</label> {{ form.photo }}{{ form.photo_file }}</p> + from django import forms -The first bit (``{{ form.photo }}``) displays the currently-selected file, -while the second (``{{ form.photo_file }}``) actually contains the file upload -form field. Thus, at the validation layer you need to check the ``photo_file`` -key. + 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) + return emails + +Let's alter the ongoing ``ContactForm`` example to demonstrate how you'd use +this in a form. Simply use ``MultiEmailField`` instead of ``forms.EmailField``, +like so:: + + class ContactForm(forms.Form): + subject = forms.CharField(max_length=100) + message = forms.CharField() + senders = MultiEmailField() + cc_myself = forms.BooleanField(required=False) + +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' ...`` + ``DateTimeInput`` ``<input type='text' ...`` + ``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. + ============================ =========================================== + +**New in Django development version:** The ``DateTimeInput`` has been added +since the last release. + +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 +---------------------------- -Finally, in your view, make sure to access ``request.FILES``, rather than -``request.POST``, for the uploaded files. This is necessary because -``request.POST`` does not contain file-upload data. +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 input 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 +default ``size`` attribute as a custom extension to the ``TextInput`` widget:: + + class CommentWidget(forms.TextInput): + def __init__(self, *args, **kwargs): + attrs = kwargs.setdefault('attrs',{}) + if 'size' not in attrs: + attrs['size'] = 40 + super(CommentWidget, self).__init__(*args, **kwargs) + +We allow the ``size`` attribute to be overridden by the user, but, by default, +this widget will behave as if ``attrs={'size': 40}`` was always passed into the +constructor. + +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 +=========================== -For example, following the ``new_data`` convention, you might do something like -this:: +The prefered way of generating forms that work with models is explained in the +`ModelForms documentation`_. - new_data = request.POST.copy() - new_data.update(request.FILES) +Looking for the ``form_for_model`` and ``form_for_instance`` documentation? +They've been deprecated, but you can still `view the documentation`_. -Validators -========== +.. _ModelForms documentation: ../modelforms/ +.. _view the documentation: ../form_for_model/ -One useful feature of manipulators is the automatic validation. Validation is -done using a simple validation API: A validator is a callable that raises a -``ValidationError`` if there's something wrong with the data. -``django.core.validators`` defines a host of validator functions (see below), -but defining your own couldn't be easier:: +Media +===== - from django.core import validators - from django import forms +Rendering an attractive and easy-to-use web form requires more than just +HTML - it also requires CSS stylesheets, and if you want to use fancy +"Web2.0" widgets, you may also need to include some JavaScript on each +page. The exact combination of CSS and JavaScript that is required for +any given page will depend upon the widgets that are in use on that page. - class ContactManipulator(forms.Manipulator): - def __init__(self): - self.fields = ( - # ... snip fields as above ... - forms.EmailField(field_name="to", validator_list=[self.isValidToAddress]) - ) +This is where Django media definitions come in. Django allows you to +associate different media files with the forms and widgets that require +that media. For example, if you want to use a calendar to render DateFields, +you can define a custom Calendar widget. This widget can then be associated +with the CSS and JavaScript that is required to render the calendar. When +the Calendar widget is used on a form, Django is able to identify the CSS and +JavaScript files that are required, and provide the list of file names +in a form suitable for easy inclusion on your web page. - def isValidToAddress(self, field_data, all_data): - if not field_data.endswith("@example.com"): - raise validators.ValidationError("You can only send messages to example.com e-mail addresses.") +.. admonition:: Media and Django Admin -Above, we've added a "to" field to the contact form, but required that the "to" -address end with "@example.com" by adding the ``isValidToAddress`` validator to -the field's ``validator_list``. + The Django Admin application defines a number of customized widgets + for calendars, filtered selections, and so on. These widgets define + media requirements, and the Django Admin uses the custom widgets + in place of the Django defaults. The Admin templates will only include + those media files that are required to render the widgets on any + given page. -The arguments to a validator function take a little explanation. ``field_data`` -is the value of the field in question, and ``all_data`` is a dictionary of all -the data being validated. + If you like the widgets that the Django Admin application uses, + feel free to use them in your own application! They're all stored + in ``django.contrib.admin.widgets``. -.. admonition:: Note:: +.. admonition:: Which JavaScript toolkit? - At the point validators are called all data will still be - strings (as ``do_html2python`` hasn't been called yet). + Many JavaScript toolkits exist, and many of them include widgets (such + as calendar widgets) that can be used to enhance your application. + Django has deliberately avoided blessing any one JavaScript toolkit. + Each toolkit has its own relative strengths and weaknesses - use + whichever toolkit suits your requirements. Django is able to integrate + with any JavaScript toolkit. -Also, because consistency in user interfaces is important, we strongly urge you -to put punctuation at the end of your validation messages. +Media as a static definition +---------------------------- -When are validators called? +The easiest way to define media is as a static definition. Using this method, +the media declaration is an inner class. The properties of the inner class +define the media requirements. + +Here's a simple example:: + + class CalendarWidget(forms.TextInput): + class Media: + css = { + 'all': ('pretty.css',) + } + js = ('animations.js', 'actions.js') + +This code defines a ``CalendarWidget``, which will be based on ``TextInput``. +Every time the CalendarWidget is used on a form, that form will be directed +to include the CSS file ``pretty.css``, and the JavaScript files +``animations.js`` and ``actions.js``. + +This static media definition is converted at runtime into a widget property +named ``media``. The media for a CalendarWidget instance can be retrieved +through this property:: + + >>> w = CalendarWidget() + >>> print w.media + <link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" /> + <script type="text/javascript" src="http://media.example.com/animations.js"></script> + <script type="text/javascript" src="http://media.example.com/actions.js"></script> + +Here's a list of all possible ``Media`` options. There are no required options. + +``css`` +~~~~~~~ + +A dictionary describing the CSS files required for various forms of output +media. + +The values in the dictionary should be a tuple/list of file names. See +`the section on media paths`_ for details of how to specify paths to media +files. + +.. _the section on media paths: `Paths in media definitions`_ + +The keys in the dictionary are the output media types. These are the same +types accepted by CSS files in media declarations: 'all', 'aural', 'braille', +'embossed', 'handheld', 'print', 'projection', 'screen', 'tty' and 'tv'. If +you need to have different stylesheets for different media types, provide +a list of CSS files for each output medium. The following example would +provide two CSS options -- one for the screen, and one for print:: + + class Media: + css = { + 'screen': ('pretty.css',), + 'print': ('newspaper.css',) + } + +If a group of CSS files are appropriate for multiple output media types, +the dictionary key can be a comma separated list of output media types. +In the following example, TV's and projectors will have the same media +requirements:: + + class Media: + css = { + 'screen': ('pretty.css',), + 'tv,projector': ('lo_res.css',), + 'print': ('newspaper.css',) + } + +If this last CSS definition were to be rendered, it would become the following HTML:: + + <link href="http://media.example.com/pretty.css" type="text/css" media="screen" rel="stylesheet" /> + <link href="http://media.example.com/lo_res.css" type="text/css" media="tv,projector" rel="stylesheet" /> + <link href="http://media.example.com/newspaper.css" type="text/css" media="print" rel="stylesheet" /> + +``js`` +~~~~~~ + +A tuple describing the required JavaScript files. See +`the section on media paths`_ for details of how to specify paths to media +files. + +``extend`` +~~~~~~~~~~ + +A boolean defining inheritance behavior for media declarations. + +By default, any object using a static media definition will inherit all the +media associated with the parent widget. This occurs regardless of how the +parent defines its media requirements. For example, if we were to extend our +basic Calendar widget from the example above:: + + class FancyCalendarWidget(CalendarWidget): + class Media: + css = { + 'all': ('fancy.css',) + } + js = ('whizbang.js',) + + >>> w = FancyCalendarWidget() + >>> print w.media + <link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" /> + <link href="http://media.example.com/fancy.css" type="text/css" media="all" rel="stylesheet" /> + <script type="text/javascript" src="http://media.example.com/animations.js"></script> + <script type="text/javascript" src="http://media.example.com/actions.js"></script> + <script type="text/javascript" src="http://media.example.com/whizbang.js"></script> + +The FancyCalendar widget inherits all the media from it's parent widget. If +you don't want media to be inherited in this way, add an ``extend=False`` +declaration to the media declaration:: + + class FancyCalendar(Calendar): + class Media: + extend = False + css = { + 'all': ('fancy.css',) + } + js = ('whizbang.js',) + + >>> w = FancyCalendarWidget() + >>> print w.media + <link href="http://media.example.com/fancy.css" type="text/css" media="all" rel="stylesheet" /> + <script type="text/javascript" src="http://media.example.com/whizbang.js"></script> + +If you require even more control over media inheritance, define your media +using a `dynamic property`_. Dynamic properties give you complete control over +which media files are inherited, and which are not. + +.. _dynamic property: `Media as a dynamic property`_ + +Media as a dynamic property --------------------------- -After a form has been submitted, Django validates each field in turn. First, -if the field is required, Django checks that it is present and non-empty. Then, -if that test passes *and the form submission contained data* for that field, all -the validators for that field are called in turn. The emphasized portion in the -last sentence is important: if a form field is not submitted (because it -contains no data -- which is normal HTML behavior), the validators are not -run against the field. - -This feature is particularly important for models using -``models.BooleanField`` or custom manipulators using things like -``forms.CheckBoxField``. If the checkbox is not selected, it will not -contribute to the form submission. - -If you would like your validator to run *always*, regardless of whether its -attached field contains any data, set the ``always_test`` attribute on the -validator function. For example:: - - def my_custom_validator(field_data, all_data): - # ... - my_custom_validator.always_test = True - -This validator will always be executed for any field it is attached to. - -Ready-made validators ---------------------- - -Writing your own validator is not difficult, but there are some situations -that come up over and over again. Django comes with a number of validators -that can be used directly in your code. All of these functions and classes -reside in ``django/core/validators.py``. - -The following validators should all be self-explanatory. Each one provides a -check for the given property: - - * isAlphaNumeric - * isAlphaNumericURL - * isSlug - * isLowerCase - * isUpperCase - * isCommaSeparatedIntegerList - * isCommaSeparatedEmailList - * isValidIPAddress4 - * isNotEmpty - * isOnlyDigits - * isNotOnlyDigits - * isInteger - * isOnlyLetters - * isValidANSIDate - * isValidANSITime - * isValidEmail - * isValidFloat - * isValidImage - * isValidImageURL - * isValidPhone - * isValidQuicktimeVideoURL - * isValidURL - * isValidHTML - * isWellFormedXml - * isWellFormedXmlFragment - * isExistingURL - * isValidUSState - * hasNoProfanities - -There are also a group of validators that are slightly more flexible. For -these validators, you create a validator instance, passing in the parameters -described below. The returned object is a callable that can be used as a -validator. +If you need to perform some more sophisticated manipulation of media +requirements, you can define the media property directly. This is done +by defining a model property that returns an instance of ``forms.Media``. +The constructor for ``forms.Media`` accepts ``css`` and ``js`` keyword +arguments in the same format as that used in a static media definition. + +For example, the static media definition for our Calendar Widget could +also be defined in a dynamic fashion:: + + class CalendarWidget(forms.TextInput): + def _media(self): + return forms.Media(css={'all': ('pretty.css',)}, + js=('animations.js', 'actions.js')) + media = property(_media) + +See the section on `Media objects`_ for more details on how to construct +return values for dynamic media properties. + +Paths in media definitions +-------------------------- + +Paths used to specify media can be either relative or absolute. If a path +starts with '/', 'http://' or 'https://', it will be interpreted as an absolute +path, and left as-is. All other paths will be prepended with the value of +``settings.MEDIA_URL``. For example, if the MEDIA_URL for your site was +``http://media.example.com/``:: + + class CalendarWidget(forms.TextInput): + class Media: + css = { + 'all': ('/css/pretty.css',), + } + js = ('animations.js', 'http://othersite.com/actions.js') + + >>> w = CalendarWidget() + >>> print w.media + <link href="/css/pretty.css" type="text/css" media="all" rel="stylesheet" /> + <script type="text/javascript" src="http://media.example.com/animations.js"></script> + <script type="text/javascript" src="http://othersite.com/actions.js"></script> + +Media objects +------------- + +When you interrogate the media attribute of a widget or form, the value that +is returned is a ``forms.Media`` object. As we have already seen, the string +representation of a Media object is the HTML required to include media +in the ``<head>`` block of your HTML page. + +However, Media objects have some other interesting properties. + +Media subsets +~~~~~~~~~~~~~ + +If you only want media of a particular type, you can use the subscript operator +to filter out a medium of interest. For example:: + + >>> w = CalendarWidget() + >>> print w.media + <link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" /> + <script type="text/javascript" src="http://media.example.com/animations.js"></script> + <script type="text/javascript" src="http://media.example.com/actions.js"></script> + + >>> print w.media['css'] + <link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" /> + +When you use the subscript operator, the value that is returned is a new +Media object -- but one that only contains the media of interest. + +Combining media objects +~~~~~~~~~~~~~~~~~~~~~~~ + +Media objects can also be added together. When two media objects are added, +the resulting Media object contains the union of the media from both files:: + + class CalendarWidget(forms.TextInput): + class Media: + css = { + 'all': ('pretty.css',) + } + js = ('animations.js', 'actions.js') + + class OtherWidget(forms.TextInput): + class Media: + js = ('whizbang.js',) + + >>> w1 = CalendarWidget() + >>> w2 = OtherWidget() + >>> print w1.media + w2.media + <link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" /> + <script type="text/javascript" src="http://media.example.com/animations.js"></script> + <script type="text/javascript" src="http://media.example.com/actions.js"></script> + <script type="text/javascript" src="http://media.example.com/whizbang.js"></script> + +Media on Forms +-------------- + +Widgets aren't the only objects that can have media definitions -- forms +can also define media. The rules for media definitions on forms are the +same as the rules for widgets: declarations can be static or dynamic; +path and inheritance rules for those declarations are exactly the same. + +Regardless of whether you define a media declaration, *all* Form objects +have a media property. The default value for this property is the result +of adding the media definitions for all widgets that are part of the form:: + + class ContactForm(forms.Form): + date = DateField(widget=CalendarWidget) + name = CharField(max_length=40, widget=OtherWidget) + + >>> f = ContactForm() + >>> f.media + <link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" /> + <script type="text/javascript" src="http://media.example.com/animations.js"></script> + <script type="text/javascript" src="http://media.example.com/actions.js"></script> + <script type="text/javascript" src="http://media.example.com/whizbang.js"></script> + +If you want to associate additional media with a form -- for example, CSS for form +layout -- simply add a media declaration to the form:: + + class ContactForm(forms.Form): + date = DateField(widget=CalendarWidget) + name = CharField(max_length=40, widget=OtherWidget) + + class Media: + css = { + 'all': ('layout.css',) + } + + >>> f = ContactForm() + >>> f.media + <link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" /> + <link href="http://media.example.com/layout.css" type="text/css" media="all" rel="stylesheet" /> + <script type="text/javascript" src="http://media.example.com/animations.js"></script> + <script type="text/javascript" src="http://media.example.com/actions.js"></script> + <script type="text/javascript" src="http://media.example.com/whizbang.js"></script> + +Formsets +======== + +A formset is a layer of abstraction to working with multiple forms on the same +page. It can be best compared to a data grid. Let's say you have the following +form:: + + >>> from django import forms + >>> class ArticleForm(forms.Form): + ... title = forms.CharField() + ... pub_date = forms.DateField() + +You might want to allow the user to create several articles at once. To create +a formset of out of an ``ArticleForm`` you would do:: + + >>> from django.forms.formsets import formset_factory + >>> ArticleFormSet = formset_factory(ArticleForm) + +You now have created a formset named ``ArticleFormSet``. The formset gives you +the ability to iterate over the forms in the formset and display them as you +would with a regular form:: + + >>> formset = ArticleFormSet() + >>> for form in formset.forms: + ... print form.as_table() + <tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" id="id_form-0-title" /></td></tr> + <tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" id="id_form-0-pub_date" /></td></tr> + +As you can see it only displayed one form. This is because by default the +``formset_factory`` defines one extra form. This can be controlled with the +``extra`` parameter:: + + >>> ArticleFormSet = formset_factory(ArticleForm, extra=2) + +Using initial data with a formset +--------------------------------- + +Initial data is what drives the main usability of a formset. As shown above +you can define the number of extra forms. What this means is that you are +telling the formset how many additional forms to show in addition to the +number of forms it generates from the initial data. Lets take a look at an +example:: + + >>> ArticleFormSet = formset_factory(ArticleForm, extra=2) + >>> formset = ArticleFormSet(initial=[ + ... {'title': u'Django is now open source', + ... 'pub_date': datetime.date.today()}, + ... ]) + + >>> for form in formset.forms: + ... print form.as_table() + <tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" value="Django is now open source" id="id_form-0-title" /></td></tr> + <tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" value="2008-05-12" id="id_form-0-pub_date" /></td></tr> + <tr><th><label for="id_form-1-title">Title:</label></th><td><input type="text" name="form-1-title" id="id_form-1-title" /></td></tr> + <tr><th><label for="id_form-1-pub_date">Pub date:</label></th><td><input type="text" name="form-1-pub_date" id="id_form-1-pub_date" /></td></tr> + <tr><th><label for="id_form-2-title">Title:</label></th><td><input type="text" name="form-2-title" id="id_form-2-title" /></td></tr> + <tr><th><label for="id_form-2-pub_date">Pub date:</label></th><td><input type="text" name="form-2-pub_date" id="id_form-2-pub_date" /></td></tr> + +There are now a total of three forms showing above. One for the initial data +that was passed in and two extra forms. Also note that we are passing in a +list of dictionaries as the initial data. + +Limiting the maximum number of forms +------------------------------------ + +The ``max_num`` parameter to ``formset_factory`` gives you the ability to +force the maximum number of forms the formset will display:: + + >>> ArticleFormSet = formset_factory(ArticleForm, extra=2, max_num=1) + >>> formset = ArticleFormset() + >>> for form in formset.forms: + ... print form.as_table() + <tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" id="id_form-0-title" /></td></tr> + <tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" id="id_form-0-pub_date" /></td></tr> + +The default value of ``max_num`` is ``0`` which is the same as saying put no +limit on the number forms displayed. + +Formset validation +------------------ + +Validation with a formset is about identical to a regular ``Form``. There is +an ``is_valid`` method on the formset to provide a convenient way to validate +each form in the formset:: + + >>> ArticleFormSet = formset_factory(ArticleForm) + >>> formset = ArticleFormSet({}) + >>> formset.is_valid() + True + +We passed in no data to the formset which is resulting in a valid form. The +formset is smart enough to ignore extra forms that were not changed. If we +attempt to provide an article, but fail to do so:: + + >>> data = { + ... 'form-TOTAL_FORMS': u'1', + ... 'form-INITIAL_FORMS': u'1', + ... 'form-0-title': u'Test', + ... 'form-0-pub_date': u'', + ... } + >>> formset = ArticleFormSet(data) + >>> formset.is_valid() + False + >>> formset.errors + [{'pub_date': [u'This field is required.']}] + +As we can see the formset properly performed validation and gave us the +expected errors. + +Understanding the ManagementForm +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You may have noticed the additional data that was required in the formset's +data above. This data is coming from the ``ManagementForm``. This form is +dealt with internally to the formset. If you don't use it, it will result in +an exception:: + + >>> data = { + ... 'form-0-title': u'Test', + ... 'form-0-pub_date': u'', + ... } + >>> formset = ArticleFormSet(data) + Traceback (most recent call last): + ... + django.forms.util.ValidationError: [u'ManagementForm data is missing or has been tampered with'] + +It is used to keep track of how many form instances are being displayed. If +you are adding new forms via JavaScript, you should increment the count fields +in this form as well. + +Custom formset validation +~~~~~~~~~~~~~~~~~~~~~~~~~ + +A formset has a ``clean`` method similar to the one on a ``Form`` class. This +is where you define your own validation that deals at the formset level:: + + >>> from django.forms.formsets import BaseFormSet + + >>> class BaseArticleFormSet(BaseFormSet): + ... def clean(self): + ... raise forms.ValidationError, u'An error occured.' + + >>> ArticleFormSet = formset_factory(ArticleForm, formset=BaseArticleFormSet) + >>> formset = ArticleFormSet({}) + >>> formset.is_valid() + False + >>> formset.non_form_errors() + [u'An error occured.'] + +The formset ``clean`` method is called after all the ``Form.clean`` methods +have been called. The errors will be found using the ``non_form_errors()`` +method on the formset. + +Dealing with ordering and deletion of forms +------------------------------------------- + +Common use cases with a formset is dealing with ordering and deletion of the +form instances. This has been dealt with for you. The ``formset_factory`` +provides two optional parameters ``can_order`` and ``can_delete`` that will do +the extra work of adding the extra fields and providing simpler ways of +getting to that data. + +``can_order`` +~~~~~~~~~~~~~ + +Default: ``False`` + +Lets create a formset with the ability to order:: + + >>> ArticleFormSet = formset_factory(ArticleForm, can_order=True) + >>> formset = ArticleFormSet(initial=[ + ... {'title': u'Article #1', 'pub_date': datetime.date(2008, 5, 10)}, + ... {'title': u'Article #2', 'pub_date': datetime.date(2008, 5, 11)}, + ... ]) + >>> for form in formset.forms: + ... print form.as_table() + <tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" value="Article #1" id="id_form-0-title" /></td></tr> + <tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" value="2008-05-10" id="id_form-0-pub_date" /></td></tr> + <tr><th><label for="id_form-0-ORDER">Order:</label></th><td><input type="text" name="form-0-ORDER" value="1" id="id_form-0-ORDER" /></td></tr> + <tr><th><label for="id_form-1-title">Title:</label></th><td><input type="text" name="form-1-title" value="Article #2" id="id_form-1-title" /></td></tr> + <tr><th><label for="id_form-1-pub_date">Pub date:</label></th><td><input type="text" name="form-1-pub_date" value="2008-05-11" id="id_form-1-pub_date" /></td></tr> + <tr><th><label for="id_form-1-ORDER">Order:</label></th><td><input type="text" name="form-1-ORDER" value="2" id="id_form-1-ORDER" /></td></tr> + <tr><th><label for="id_form-2-title">Title:</label></th><td><input type="text" name="form-2-title" id="id_form-2-title" /></td></tr> + <tr><th><label for="id_form-2-pub_date">Pub date:</label></th><td><input type="text" name="form-2-pub_date" id="id_form-2-pub_date" /></td></tr> + <tr><th><label for="id_form-2-ORDER">Order:</label></th><td><input type="text" name="form-2-ORDER" id="id_form-2-ORDER" /></td></tr> + +This adds an additional field to each form. This new field is named ``ORDER`` +and is an ``forms.IntegerField``. For the forms that came from the initial +data it automatically assigned them a numeric value. Lets look at what will +happen when the user changes these values:: + + >>> data = { + ... 'form-TOTAL_FORMS': u'3', + ... 'form-INITIAL_FORMS': u'2', + ... 'form-0-title': u'Article #1', + ... 'form-0-pub_date': u'2008-05-10', + ... 'form-0-ORDER': u'2', + ... 'form-1-title': u'Article #2', + ... 'form-1-pub_date': u'2008-05-11', + ... 'form-1-ORDER': u'1', + ... 'form-2-title': u'Article #3', + ... 'form-2-pub_date': u'2008-05-01', + ... 'form-2-ORDER': u'0', + ... } + + >>> formset = ArticleFormSet(data, initial=[ + ... {'title': u'Article #1', 'pub_date': datetime.date(2008, 5, 10)}, + ... {'title': u'Article #2', 'pub_date': datetime.date(2008, 5, 11)}, + ... ]) + >>> formset.is_valid() + True + >>> for form in formset.ordered_forms: + ... print form.cleaned_data + {'pub_date': datetime.date(2008, 5, 1), 'ORDER': 0, 'title': u'Article #3'} + {'pub_date': datetime.date(2008, 5, 11), 'ORDER': 1, 'title': u'Article #2'} + {'pub_date': datetime.date(2008, 5, 10), 'ORDER': 2, 'title': u'Article #1'} + +``can_delete`` +~~~~~~~~~~~~~~ + +Default: ``False`` + +Lets create a formset with the ability to delete:: + + >>> ArticleFormSet = formset_factory(ArticleForm, can_delete=True) + >>> formset = ArticleFormSet(initial=[ + ... {'title': u'Article #1', 'pub_date': datetime.date(2008, 5, 10)}, + ... {'title': u'Article #2', 'pub_date': datetime.date(2008, 5, 11)}, + ... ]) + >>> for form in formset.forms: + .... print form.as_table() + <input type="hidden" name="form-TOTAL_FORMS" value="3" id="id_form-TOTAL_FORMS" /><input type="hidden" name="form-INITIAL_FORMS" value="2" id="id_form-INITIAL_FORMS" /> + <tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" value="Article #1" id="id_form-0-title" /></td></tr> + <tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" value="2008-05-10" id="id_form-0-pub_date" /></td></tr> + <tr><th><label for="id_form-0-DELETE">Delete:</label></th><td><input type="checkbox" name="form-0-DELETE" id="id_form-0-DELETE" /></td></tr> + <tr><th><label for="id_form-1-title">Title:</label></th><td><input type="text" name="form-1-title" value="Article #2" id="id_form-1-title" /></td></tr> + <tr><th><label for="id_form-1-pub_date">Pub date:</label></th><td><input type="text" name="form-1-pub_date" value="2008-05-11" id="id_form-1-pub_date" /></td></tr> + <tr><th><label for="id_form-1-DELETE">Delete:</label></th><td><input type="checkbox" name="form-1-DELETE" id="id_form-1-DELETE" /></td></tr> + <tr><th><label for="id_form-2-title">Title:</label></th><td><input type="text" name="form-2-title" id="id_form-2-title" /></td></tr> + <tr><th><label for="id_form-2-pub_date">Pub date:</label></th><td><input type="text" name="form-2-pub_date" id="id_form-2-pub_date" /></td></tr> + <tr><th><label for="id_form-2-DELETE">Delete:</label></th><td><input type="checkbox" name="form-2-DELETE" id="id_form-2-DELETE" /></td></tr> + +Similar to ``can_order`` this adds a new field to each form named ``DELETE`` +and is a ``forms.BooleanField``. When data comes through marking any of the +delete fields you can access them with ``deleted_forms``:: + + >>> data = { + ... 'form-TOTAL_FORMS': u'3', + ... 'form-INITIAL_FORMS': u'2', + ... 'form-0-title': u'Article #1', + ... 'form-0-pub_date': u'2008-05-10', + ... 'form-0-DELETE': u'on', + ... 'form-1-title': u'Article #2', + ... 'form-1-pub_date': u'2008-05-11', + ... 'form-1-DELETE': u'', + ... 'form-2-title': u'', + ... 'form-2-pub_date': u'', + ... 'form-2-DELETE': u'', + ... } + + >>> formset = ArticleFormSet(data, initial=[ + ... {'title': u'Article #1', 'pub_date': datetime.date(2008, 5, 10)}, + ... {'title': u'Article #2', 'pub_date': datetime.date(2008, 5, 11)}, + ... ]) + >>> [form.cleaned_data for form in formset.deleted_forms] + [{'DELETE': True, 'pub_date': datetime.date(2008, 5, 10), 'title': u'Article #1'}] + +Adding additional fields to a formset +------------------------------------- + +If you need to add additional fields to the formset this can be easily +accomplished. The formset base class provides an ``add_fields`` method. You +can simply override this method to add your own fields or even redefine the +default fields/attributes of the order and deletion fields:: + + >>> class BaseArticleFormSet(BaseFormSet): + ... def add_fields(self, form, index): + ... super(BaseArticleFormSet, self).add_fields(form, index) + ... form.fields["my_field"] = forms.CharField() + + >>> ArticleFormSet = formset_factory(ArticleForm, formset=BaseArticleFormSet) + >>> formset = ArticleFormSet() + >>> for form in formset.forms: + ... print form.as_table() + <tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" id="id_form-0-title" /></td></tr> + <tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" id="id_form-0-pub_date" /></td></tr> + <tr><th><label for="id_form-0-my_field">My field:</label></th><td><input type="text" name="form-0-my_field" id="id_form-0-my_field" /></td></tr> + +Using a formset in views and templates +-------------------------------------- + +Using a formset inside a view is as easy as using a regular ``Form`` class. +The only thing you will want to be aware of is making sure to use the +management form inside the template. Lets look at a sample view:: + + def manage_articles(request): + ArticleFormSet = formset_factory(ArticleForm) + if request.method == 'POST': + formset = ArticleFormSet(request.POST, request.FILES) + if formset.is_valid(): + # do something with the formset.cleaned_data + else: + formset = ArticleFormSet() + return render_to_response('manage_articles.html', {'formset': formset}) + +The ``manage_articles.html`` template might look like this:: + + <form method="POST" action=""> + {{ formset.management_form }} + <table> + {% for form in formset.forms %} + {{ form }} + {% endfor %} + </table> + </form> -For example:: +However the above can be slightly shortcutted and let the formset itself deal +with the management form:: - from django.core import validators - from django import forms + <form method="POST" action=""> + <table> + {{ formset }} + </table> + </form> - power_validator = validators.IsAPowerOf(2) - - class InstallationManipulator(forms.Manipulator) - def __init__(self): - self.fields = ( - ... - forms.IntegerField(field_name = "size", validator_list=[power_validator]) - ) - -Here, ``validators.IsAPowerOf(...)`` returned something that could be used as -a validator (in this case, a check that a number was a power of 2). - -Each of the standard validators that take parameters have an optional final -argument (``error_message``) that is the message returned when validation -fails. If no message is passed in, a default message is used. - -``AlwaysMatchesOtherField`` - Takes a field name and the current field is valid if and only if its value - matches the contents of the other field. - -``ValidateIfOtherFieldEquals`` - Takes three parameters: ``other_field``, ``other_value`` and - ``validator_list``, in that order. If ``other_field`` has a value of - ``other_value``, then the validators in ``validator_list`` are all run - against the current field. - -``RequiredIfOtherFieldGiven`` - Takes a field name of the current field is only required if the other - field has a value. - -``RequiredIfOtherFieldsGiven`` - Similar to ``RequiredIfOtherFieldGiven``, except that it takes a list of - field names and if any one of the supplied fields has a value provided, - the current field being validated is required. - -``RequiredIfOtherFieldNotGiven`` - Takes the name of the other field and this field is only required if the - other field has no value. - -``RequiredIfOtherFieldEquals`` and ``RequiredIfOtherFieldDoesNotEqual`` - Each of these validator classes takes a field name and a value (in that - order). If the given field does (or does not have, in the latter case) the - given value, then the current field being validated is required. - - An optional ``other_label`` argument can be passed which, if given, is used - in error messages instead of the value. This allows more user friendly error - messages if the value itself is not descriptive enough. - - Note that because validators are called before any ``do_html2python()`` - functions, the value being compared against is a string. So - ``RequiredIfOtherFieldEquals('choice', '1')`` is correct, whilst - ``RequiredIfOtherFieldEquals('choice', 1)`` will never result in the - equality test succeeding. - -``IsLessThanOtherField`` - Takes a field name and validates that the current field being validated - has a value that is less than (or equal to) the other field's value. - Again, comparisons are done using strings, so be cautious about using - this function to compare data that should be treated as another type. The - string "123" is less than the string "2", for example. If you don't want - string comparison here, you will need to write your own validator. - -``NumberIsInRange`` - Takes two boundary numbers, ``lower`` and ``upper``, and checks that the - field is greater than ``lower`` (if given) and less than ``upper`` (if - given). - - Both checks are inclusive. That is, ``NumberIsInRange(10, 20)`` will allow - values of both 10 and 20. This validator only checks numeric values - (e.g., float and integer values). - -``IsAPowerOf`` - Takes an integer argument and when called as a validator, checks that the - field being validated is a power of the integer. - -``IsValidDecimal`` - Takes a maximum number of digits and number of decimal places (in that - order) and validates whether the field is a decimal with no more than the - maximum number of digits and decimal places. - -``MatchesRegularExpression`` - Takes a regular expression (a string) as a parameter and validates the - field value against it. - -``AnyValidator`` - Takes a list of validators as a parameter. At validation time, if the - field successfully validates against any one of the validators, it passes - validation. The validators are tested in the order specified in the - original list. - -``URLMimeTypeCheck`` - Used to validate URL fields. Takes a list of MIME types (such as - ``text/plain``) at creation time. At validation time, it verifies that the - field is indeed a URL and then tries to retrieve the content at the URL. - Validation succeeds if the content could be retrieved and it has a content - type from the list used to create the validator. - -``RelaxNGCompact`` - Used to validate an XML document against a Relax NG compact schema. Takes - a file path to the location of the schema and an optional root element - (which is wrapped around the XML fragment before validation, if supplied). - At validation time, the XML fragment is validated against the schema using - the executable specified in the ``JING_PATH`` setting (see the settings_ - document for more details). - -.. _`generic views`: ../generic_views/ -.. _`models API`: ../model-api/ -.. _settings: ../settings/ +The above ends up calling the ``as_table`` method on the formset class. diff --git a/docs/generic_views.txt b/docs/generic_views.txt index a7602524a9..8c0fec2ded 100644 --- a/docs/generic_views.txt +++ b/docs/generic_views.txt @@ -83,24 +83,29 @@ and issuing a redirect. ``django.views.generic.simple.direct_to_template`` -------------------------------------------------- -**Description:** +Description +~~~~~~~~~~~ Renders a given template, passing it a ``{{ params }}`` template variable, which is a dictionary of the parameters captured in the URL. -**Required arguments:** +Required arguments +~~~~~~~~~~~~~~~~~~ - * ``template``: The full name of a template to use. +``template`` + The full name of a template to use. -**Optional arguments:** +Optional arguments +~~~~~~~~~~~~~~~~~~ - * ``extra_context``: A dictionary of values to add to the template - context. By default, this is an empty dictionary. If a value in the - dictionary is callable, the generic view will call it - just before rendering the template. +``extra_context`` + A dictionary of values to add to the template context. By default, this is + an empty dictionary. If a value in the dictionary is callable, the generic + view will call it just before rendering the template. - * ``mimetype``: The MIME type to use for the resulting document. Defaults - to the value of the ``DEFAULT_CONTENT_TYPE`` setting. +``mimetype`` + The MIME type to use for the resulting document. Defaults to the value of + the ``DEFAULT_CONTENT_TYPE`` setting. **Example:** @@ -118,7 +123,8 @@ variable ``{{ params.id }}`` that is set to ``15``. ``django.views.generic.simple.redirect_to`` ------------------------------------------- -**Description:** +Description +~~~~~~~~~~~ Redirects to a given URL. @@ -127,10 +133,12 @@ interpolated against the parameters captured in the URL. If the given URL is ``None``, Django will return an ``HttpResponseGone`` (410). -**Required arguments:** +Required arguments +~~~~~~~~~~~~~~~~~~ - * ``url``: The URL to redirect to, as a string. Or ``None`` to raise a 410 - (Gone) HTTP error. +``url`` + The URL to redirect to, as a string. Or ``None`` to raise a 410 (Gone) + HTTP error. **Example:** @@ -155,57 +163,69 @@ are views for displaying drilldown pages for date-based data. ``django.views.generic.date_based.archive_index`` ------------------------------------------------- -**Description:** +Description +~~~~~~~~~~~ A top-level index page showing the "latest" objects, by date. Objects with a date in the *future* are not included unless you set ``allow_future`` to ``True``. -**Required arguments:** +Required arguments +~~~~~~~~~~~~~~~~~~ - * ``queryset``: A ``QuerySet`` of objects for which the archive serves. +``queryset`` + A ``QuerySet`` of objects for which the archive serves. - * ``date_field``: The name of the ``DateField`` or ``DateTimeField`` in - the ``QuerySet``'s model that the date-based archive should use to - determine the objects on the page. +``date_field`` + The name of the ``DateField`` or ``DateTimeField`` in the ``QuerySet``'s + model that the date-based archive should use to determine the objects on + the page. -**Optional arguments:** +Optional arguments +~~~~~~~~~~~~~~~~~~ - * ``num_latest``: The number of latest objects to send to the template - context. By default, it's 15. +``num_latest`` + The number of latest objects to send to the template context. By default, + it's 15. - * ``template_name``: The full name of a template to use in rendering the - page. This lets you override the default template name (see below). +``template_name`` + The full name of a template to use in rendering the page. This lets you + override the default template name (see below). - * ``template_loader``: The template loader to use when loading the - template. By default, it's ``django.template.loader``. +``template_loader`` + The template loader to use when loading the template. By default, it's + ``django.template.loader``. - * ``extra_context``: A dictionary of values to add to the template - context. By default, this is an empty dictionary. If a value in the - dictionary is callable, the generic view will call it - just before rendering the template. +``extra_context`` + A dictionary of values to add to the template context. By default, this is + an empty dictionary. If a value in the dictionary is callable, the generic + view will call it just before rendering the template. - * ``allow_empty``: A boolean specifying whether to display the page if no - objects are available. If this is ``False`` and no objects are available, - the view will raise a 404 instead of displaying an empty page. By - default, this is ``True``. +``allow_empty`` + A boolean specifying whether to display the page if no objects are + available. If this is ``False`` and no objects are available, the view will + raise a 404 instead of displaying an empty page. By default, this is + ``True``. - * ``context_processors``: A list of template-context processors to apply to - the view's template. See the `RequestContext docs`_. +``context_processors`` + A list of template-context processors to apply to the view's template. See + the `RequestContext docs`_. - * ``mimetype``: The MIME type to use for the resulting document. Defaults - to the value of the ``DEFAULT_CONTENT_TYPE`` setting. +``mimetype`` + The MIME type to use for the resulting document. Defaults to the value of + the ``DEFAULT_CONTENT_TYPE`` setting. - * ``allow_future``: A boolean specifying whether to include "future" - objects on this page, where "future" means objects in which the field - specified in ``date_field`` is greater than the current date/time. By - default, this is ``False``. +``allow_future`` + A boolean specifying whether to include "future" objects on this page, + where "future" means objects in which the field specified in ``date_field`` + is greater than the current date/time. By default, this is ``False``. - * **New in Django development version:** ``template_object_name``: - Designates the name of the template variable to use in the template - context. By default, this is ``'latest'``. +``template_object_name`` (**New in Django development version**) + Designates the name of the template variable to use in the template + context. By default, this is ``'latest'``. -**Template name:** +Template name +~~~~~~~~~~~~~ If ``template_name`` isn't specified, this view will use the template ``<app_label>/<model_name>_archive.html`` by default, where: @@ -217,365 +237,435 @@ If ``template_name`` isn't specified, this view will use the template your model's app. For example, if your model lives in ``apps/blog/models.py``, that'd be ``blog``. -**Template context:** +Template context +~~~~~~~~~~~~~~~~ In addition to ``extra_context``, the template's context will be: - * ``date_list``: A list of ``datetime.date`` objects representing all - years that have objects available according to ``queryset``. These are - ordered in reverse. This is equivalent to - ``queryset.dates(date_field, 'year')[::-1]``. +``date_list`` + A list of ``datetime.date`` objects representing all years that have + objects available according to ``queryset``. These are ordered in reverse. + This is equivalent to ``queryset.dates(date_field, 'year')[::-1]``. - * ``latest``: The ``num_latest`` objects in the system, ordered descending - by ``date_field``. For example, if ``num_latest`` is ``10``, then - ``latest`` will be a list of the latest 10 objects in ``queryset``. +``latest`` + The ``num_latest`` objects in the system, ordered descending by + ``date_field``. For example, if ``num_latest`` is ``10``, then ``latest`` + will be a list of the latest 10 objects in ``queryset``. - **New in Django development version:** This variable's name depends on - the ``template_object_name`` parameter, which is ``'latest'`` by default. - If ``template_object_name`` is ``'foo'``, this variable's name will be - ``foo``. + **New in Django development version:** This variable's name depends on the + ``template_object_name`` parameter, which is ``'latest'`` by default. + If ``template_object_name`` is ``'foo'``, this variable's name will be + ``foo``. .. _RequestContext docs: ../templates_python/#subclassing-context-requestcontext ``django.views.generic.date_based.archive_year`` ------------------------------------------------ -**Description:** +Description +~~~~~~~~~~~ A yearly archive page showing all available months in a given year. Objects with a date in the *future* are not displayed unless you set ``allow_future`` to ``True``. -**Required arguments:** +Required arguments +~~~~~~~~~~~~~~~~~~ - * ``year``: The four-digit year for which the archive serves. +``year`` + The four-digit year for which the archive serves. - * ``queryset``: A ``QuerySet`` of objects for which the archive serves. +``queryset`` + A ``QuerySet`` of objects for which the archive serves. - * ``date_field``: The name of the ``DateField`` or ``DateTimeField`` in - the ``QuerySet``'s model that the date-based archive should use to - determine the objects on the page. +``date_field`` + The name of the ``DateField`` or ``DateTimeField`` in the ``QuerySet``'s + model that the date-based archive should use to determine the objects on + the page. -**Optional arguments:** +Optional arguments +~~~~~~~~~~~~~~~~~~ - * ``template_name``: The full name of a template to use in rendering the - page. This lets you override the default template name (see below). +``template_name`` + The full name of a template to use in rendering the page. This lets you + override the default template name (see below). - * ``template_loader``: The template loader to use when loading the - template. By default, it's ``django.template.loader``. +``template_loader`` + The template loader to use when loading the template. By default, it's + ``django.template.loader``. - * ``extra_context``: A dictionary of values to add to the template - context. By default, this is an empty dictionary. If a value in the - dictionary is callable, the generic view will call it - just before rendering the template. +``extra_context`` + A dictionary of values to add to the template context. By default, this is + an empty dictionary. If a value in the dictionary is callable, the generic + view will call it just before rendering the template. - * ``allow_empty``: A boolean specifying whether to display the page if no - objects are available. If this is ``False`` and no objects are available, - the view will raise a 404 instead of displaying an empty page. By - default, this is ``False``. +``allow_empty`` + A boolean specifying whether to display the page if no objects are + available. If this is ``False`` and no objects are available, the view will + raise a 404 instead of displaying an empty page. By default, this is + ``False``. - * ``context_processors``: A list of template-context processors to apply to - the view's template. See the `RequestContext docs`_. +``context_processors`` + A list of template-context processors to apply to the view's template. See + the `RequestContext docs`_. - * ``template_object_name``: Designates the name of the template variable - to use in the template context. By default, this is ``'object'``. The - view will append ``'_list'`` to the value of this parameter in - determining the variable's name. +``template_object_name`` + Designates the name of the template variable to use in the template + context. By default, this is ``'object'``. The view will append ``'_list'`` + to the value of this parameter in determining the variable's name. - * ``make_object_list``: A boolean specifying whether to retrieve the full - list of objects for this year and pass those to the template. If ``True``, - this list of objects will be made available to the template as - ``object_list``. (The name ``object_list`` may be different; see the docs - for ``object_list`` in the "Template context" section below.) By default, - this is ``False``. +``make_object_list`` + A boolean specifying whether to retrieve the full list of objects for this + year and pass those to the template. If ``True``, this list of objects will + be made available to the template as ``object_list``. (The name + ``object_list`` may be different; see the docs for ``object_list`` in the + "Template context" section below.) By default, this is ``False``. - * ``mimetype``: The MIME type to use for the resulting document. Defaults - to the value of the ``DEFAULT_CONTENT_TYPE`` setting. +``mimetype`` + The MIME type to use for the resulting document. Defaults to the value of + the ``DEFAULT_CONTENT_TYPE`` setting. - * ``allow_future``: A boolean specifying whether to include "future" - objects on this page, where "future" means objects in which the field - specified in ``date_field`` is greater than the current date/time. By - default, this is ``False``. +``allow_future`` + A boolean specifying whether to include "future" objects on this page, + where "future" means objects in which the field specified in ``date_field`` + is greater than the current date/time. By default, this is ``False``. -**Template name:** +Template name +~~~~~~~~~~~~~ If ``template_name`` isn't specified, this view will use the template ``<app_label>/<model_name>_archive_year.html`` by default. -**Template context:** +Template context +~~~~~~~~~~~~~~~~ In addition to ``extra_context``, the template's context will be: - * ``date_list``: A list of ``datetime.date`` objects representing all - months that have objects available in the given year, according to - ``queryset``, in ascending order. +``date_list`` + A list of ``datetime.date`` objects representing all months that have + objects available in the given year, according to ``queryset``, in + ascending order. - * ``year``: The given year, as a four-character string. +``year`` + The given year, as a four-character string. - * ``object_list``: If the ``make_object_list`` parameter is ``True``, this - will be set to a list of objects available for the given year, ordered by - the date field. This variable's name depends on the - ``template_object_name`` parameter, which is ``'object'`` by default. If - ``template_object_name`` is ``'foo'``, this variable's name will be - ``foo_list``. +``object_list`` + If the ``make_object_list`` parameter is ``True``, this will be set to a + list of objects available for the given year, ordered by the date field. + This variable's name depends on the ``template_object_name`` parameter, + which is ``'object'`` by default. If ``template_object_name`` is ``'foo'``, + this variable's name will be ``foo_list``. - If ``make_object_list`` is ``False``, ``object_list`` will be passed to - the template as an empty list. + If ``make_object_list`` is ``False``, ``object_list`` will be passed to the + template as an empty list. ``django.views.generic.date_based.archive_month`` ------------------------------------------------- -**Description:** +Description +~~~~~~~~~~~ A monthly archive page showing all objects in a given month. Objects with a date in the *future* are not displayed unless you set ``allow_future`` to ``True``. -**Required arguments:** +Required arguments +~~~~~~~~~~~~~~~~~~ - * ``year``: The four-digit year for which the archive serves (a string). +``year`` + The four-digit year for which the archive serves (a string). - * ``month``: The month for which the archive serves, formatted according to - the ``month_format`` argument. +``month`` + The month for which the archive serves, formatted according to the + ``month_format`` argument. - * ``queryset``: A ``QuerySet`` of objects for which the archive serves. +``queryset`` + A ``QuerySet`` of objects for which the archive serves. - * ``date_field``: The name of the ``DateField`` or ``DateTimeField`` in - the ``QuerySet``'s model that the date-based archive should use to - determine the objects on the page. +``date_field`` + The name of the ``DateField`` or ``DateTimeField`` in the ``QuerySet``'s + model that the date-based archive should use to determine the objects on + the page. -**Optional arguments:** +Optional arguments +~~~~~~~~~~~~~~~~~~ - * ``month_format``: A format string that regulates what format the - ``month`` parameter uses. This should be in the syntax accepted by - Python's ``time.strftime``. (See the `strftime docs`_.) It's set to - ``"%b"`` by default, which is a three-letter month abbreviation. To - change it to use numbers, use ``"%m"``. +``month_format`` + A format string that regulates what format the ``month`` parameter uses. + This should be in the syntax accepted by Python's ``time.strftime``. (See + the `strftime docs`_.) It's set to ``"%b"`` by default, which is a three- + letter month abbreviation. To change it to use numbers, use ``"%m"``. - * ``template_name``: The full name of a template to use in rendering the - page. This lets you override the default template name (see below). +``template_name`` + The full name of a template to use in rendering the page. This lets you + override the default template name (see below). - * ``template_loader``: The template loader to use when loading the - template. By default, it's ``django.template.loader``. +``template_loader`` + The template loader to use when loading the template. By default, it's + ``django.template.loader``. - * ``extra_context``: A dictionary of values to add to the template - context. By default, this is an empty dictionary. If a value in the - dictionary is callable, the generic view will call it - just before rendering the template. +``extra_context`` + A dictionary of values to add to the template context. By default, this is + an empty dictionary. If a value in the dictionary is callable, the generic + view will call it just before rendering the template. - * ``allow_empty``: A boolean specifying whether to display the page if no - objects are available. If this is ``False`` and no objects are available, - the view will raise a 404 instead of displaying an empty page. By - default, this is ``False``. +``allow_empty`` + A boolean specifying whether to display the page if no objects are + available. If this is ``False`` and no objects are available, the view will + raise a 404 instead of displaying an empty page. By default, this is + ``False``. - * ``context_processors``: A list of template-context processors to apply to - the view's template. See the `RequestContext docs`_. +``context_processors`` + A list of template-context processors to apply to the view's template. See + the `RequestContext docs`_. - * ``template_object_name``: Designates the name of the template variable - to use in the template context. By default, this is ``'object'``. The - view will append ``'_list'`` to the value of this parameter in - determining the variable's name. +``template_object_name`` + Designates the name of the template variable to use in the template + context. By default, this is ``'object'``. The view will append ``'_list'`` + to the value of this parameter in determining the variable's name. - * ``mimetype``: The MIME type to use for the resulting document. Defaults - to the value of the ``DEFAULT_CONTENT_TYPE`` setting. +``mimetype`` + The MIME type to use for the resulting document. Defaults to the value of + the ``DEFAULT_CONTENT_TYPE`` setting. - * ``allow_future``: A boolean specifying whether to include "future" - objects on this page, where "future" means objects in which the field - specified in ``date_field`` is greater than the current date/time. By - default, this is ``False``. +``allow_future`` + A boolean specifying whether to include "future" objects on this page, + where "future" means objects in which the field specified in ``date_field`` + is greater than the current date/time. By default, this is ``False``. -**Template name:** +Template name +~~~~~~~~~~~~~ If ``template_name`` isn't specified, this view will use the template ``<app_label>/<model_name>_archive_month.html`` by default. -**Template context:** +Template context +~~~~~~~~~~~~~~~~ In addition to ``extra_context``, the template's context will be: - * ``month``: A ``datetime.date`` object representing the given month. +``month`` + A ``datetime.date`` object representing the given month. - * ``next_month``: A ``datetime.date`` object representing the first day of - the next month. If the next month is in the future, this will be - ``None``. +``next_month`` + A ``datetime.date`` object representing the first day of the next month. If + the next month is in the future, this will be ``None``. - * ``previous_month``: A ``datetime.date`` object representing the first day - of the previous month. Unlike ``next_month``, this will never be - ``None``. +``previous_month`` + A ``datetime.date`` object representing the first day of the previous + month. Unlike ``next_month``, this will never be ``None``. - * ``object_list``: A list of objects available for the given month. This - variable's name depends on the ``template_object_name`` parameter, which - is ``'object'`` by default. If ``template_object_name`` is ``'foo'``, - this variable's name will be ``foo_list``. +``object_list`` + A list of objects available for the given month. This variable's name + depends on the ``template_object_name`` parameter, which is ``'object'`` by + default. If ``template_object_name`` is ``'foo'``, this variable's name + will be ``foo_list``. .. _strftime docs: http://www.python.org/doc/current/lib/module-time.html#l2h-1941 ``django.views.generic.date_based.archive_week`` ------------------------------------------------ -**Description:** +Description +~~~~~~~~~~~ A weekly archive page showing all objects in a given week. Objects with a date in the *future* are not displayed unless you set ``allow_future`` to ``True``. -**Required arguments:** +Required arguments +~~~~~~~~~~~~~~~~~~ - * ``year``: The four-digit year for which the archive serves (a string). +``year`` + The four-digit year for which the archive serves (a string). - * ``week``: The week of the year for which the archive serves (a string). - Weeks start with Sunday. +``week`` + The week of the year for which the archive serves (a string). Weeks start + with Sunday. - * ``queryset``: A ``QuerySet`` of objects for which the archive serves. +``queryset`` + A ``QuerySet`` of objects for which the archive serves. - * ``date_field``: The name of the ``DateField`` or ``DateTimeField`` in - the ``QuerySet``'s model that the date-based archive should use to - determine the objects on the page. +``date_field`` + The name of the ``DateField`` or ``DateTimeField`` in the ``QuerySet``'s + model that the date-based archive should use to determine the objects on + the page. -**Optional arguments:** +Optional arguments +~~~~~~~~~~~~~~~~~~ - * ``template_name``: The full name of a template to use in rendering the - page. This lets you override the default template name (see below). +``template_name`` + The full name of a template to use in rendering the page. This lets you + override the default template name (see below). - * ``template_loader``: The template loader to use when loading the - template. By default, it's ``django.template.loader``. +``template_loader`` + The template loader to use when loading the template. By default, it's + ``django.template.loader``. - * ``extra_context``: A dictionary of values to add to the template - context. By default, this is an empty dictionary. If a value in the - dictionary is callable, the generic view will call it - just before rendering the template. +``extra_context`` + A dictionary of values to add to the template context. By default, this is + an empty dictionary. If a value in the dictionary is callable, the generic + view will call it just before rendering the template. - * ``allow_empty``: A boolean specifying whether to display the page if no - objects are available. If this is ``False`` and no objects are available, - the view will raise a 404 instead of displaying an empty page. By - default, this is ``True``. +``allow_empty`` + A boolean specifying whether to display the page if no objects are + available. If this is ``False`` and no objects are available, the view will + raise a 404 instead of displaying an empty page. By default, this is + ``True``. - * ``context_processors``: A list of template-context processors to apply to - the view's template. See the `RequestContext docs`_. +``context_processors`` + A list of template-context processors to apply to the view's template. See + the `RequestContext docs`_. - * ``template_object_name``: Designates the name of the template variable - to use in the template context. By default, this is ``'object'``. The - view will append ``'_list'`` to the value of this parameter in - determining the variable's name. +``template_object_name`` + Designates the name of the template variable to use in the template + context. By default, this is ``'object'``. The view will append ``'_list'`` + to the value of this parameter in determining the variable's name. - * ``mimetype``: The MIME type to use for the resulting document. Defaults - to the value of the ``DEFAULT_CONTENT_TYPE`` setting. +``mimetype`` + The MIME type to use for the resulting document. Defaults to the value of + the ``DEFAULT_CONTENT_TYPE`` setting. - * ``allow_future``: A boolean specifying whether to include "future" - objects on this page, where "future" means objects in which the field - specified in ``date_field`` is greater than the current date/time. By - default, this is ``False``. +``allow_future`` + A boolean specifying whether to include "future" objects on this page, + where "future" means objects in which the field specified in ``date_field`` + is greater than the current date/time. By default, this is ``False``. -**Template name:** +Template name +~~~~~~~~~~~~~ If ``template_name`` isn't specified, this view will use the template ``<app_label>/<model_name>_archive_week.html`` by default. -**Template context:** +Template context +~~~~~~~~~~~~~~~~ In addition to ``extra_context``, the template's context will be: - * ``week``: A ``datetime.date`` object representing the first day of the - given week. +``week`` + A ``datetime.date`` object representing the first day of the given week. - * ``object_list``: A list of objects available for the given week. This - variable's name depends on the ``template_object_name`` parameter, which - is ``'object'`` by default. If ``template_object_name`` is ``'foo'``, - this variable's name will be ``foo_list``. +``object_list`` + A list of objects available for the given week. This variable's name + depends on the ``template_object_name`` parameter, which is ``'object'`` by + default. If ``template_object_name`` is ``'foo'``, this variable's name + will be ``foo_list``. ``django.views.generic.date_based.archive_day`` ----------------------------------------------- -**Description:** +Description +~~~~~~~~~~~ A day archive page showing all objects in a given day. Days in the future throw a 404 error, regardless of whether any objects exist for future days, unless you set ``allow_future`` to ``True``. -**Required arguments:** +Required arguments +~~~~~~~~~~~~~~~~~~ - * ``year``: The four-digit year for which the archive serves (a string). +``year`` + The four-digit year for which the archive serves (a string). - * ``month``: The month for which the archive serves, formatted according to - the ``month_format`` argument. +``month`` + The month for which the archive serves, formatted according to the + ``month_format`` argument. - * ``day``: The day for which the archive serves, formatted according to the - ``day_format`` argument. +``day`` + The day for which the archive serves, formatted according to the + ``day_format`` argument. - * ``queryset``: A ``QuerySet`` of objects for which the archive serves. +``queryset`` + A ``QuerySet`` of objects for which the archive serves. - * ``date_field``: The name of the ``DateField`` or ``DateTimeField`` in - the ``QuerySet``'s model that the date-based archive should use to - determine the objects on the page. +``date_field`` + The name of the ``DateField`` or ``DateTimeField`` in the ``QuerySet``'s + model that the date-based archive should use to determine the objects on + the page. -**Optional arguments:** +Optional arguments +~~~~~~~~~~~~~~~~~~ - * ``month_format``: A format string that regulates what format the - ``month`` parameter uses. This should be in the syntax accepted by - Python's ``time.strftime``. (See the `strftime docs`_.) It's set to - ``"%b"`` by default, which is a three-letter month abbreviation. To - change it to use numbers, use ``"%m"``. +``month_format`` + A format string that regulates what format the ``month`` parameter uses. + This should be in the syntax accepted by Python's ``time.strftime``. (See + the `strftime docs`_.) It's set to ``"%b"`` by default, which is a three- + letter month abbreviation. To change it to use numbers, use ``"%m"``. - * ``day_format``: Like ``month_format``, but for the ``day`` parameter. - It defaults to ``"%d"`` (day of the month as a decimal number, 01-31). +``day_format`` + Like ``month_format``, but for the ``day`` parameter. It defaults to + ``"%d"`` (day of the month as a decimal number, 01-31). - * ``template_name``: The full name of a template to use in rendering the - page. This lets you override the default template name (see below). +``template_name`` + The full name of a template to use in rendering the page. This lets you + override the default template name (see below). - * ``template_loader``: The template loader to use when loading the - template. By default, it's ``django.template.loader``. +``template_loader`` + The template loader to use when loading the template. By default, it's + ``django.template.loader``. - * ``extra_context``: A dictionary of values to add to the template - context. By default, this is an empty dictionary. If a value in the - dictionary is callable, the generic view will call it - just before rendering the template. +``extra_context`` + A dictionary of values to add to the template context. By default, this is + an empty dictionary. If a value in the dictionary is callable, the generic + view will call it just before rendering the template. - * ``allow_empty``: A boolean specifying whether to display the page if no - objects are available. If this is ``False`` and no objects are available, - the view will raise a 404 instead of displaying an empty page. By - default, this is ``False``. +``allow_empty`` + A boolean specifying whether to display the page if no objects are + available. If this is ``False`` and no objects are available, the view will + raise a 404 instead of displaying an empty page. By default, this is + ``False``. - * ``context_processors``: A list of template-context processors to apply to - the view's template. See the `RequestContext docs`_. +``context_processors`` + A list of template-context processors to apply to the view's template. See + the `RequestContext docs`_. - * ``template_object_name``: Designates the name of the template variable - to use in the template context. By default, this is ``'object'``. The - view will append ``'_list'`` to the value of this parameter in - determining the variable's name. +``template_object_name`` + Designates the name of the template variable to use in the template + context. By default, this is ``'object'``. The view will append + ``'_list'`` to the value of this parameter in determining the variable's + name. - * ``mimetype``: The MIME type to use for the resulting document. Defaults - to the value of the ``DEFAULT_CONTENT_TYPE`` setting. +``mimetype`` + The MIME type to use for the resulting document. Defaults to the value of + the ``DEFAULT_CONTENT_TYPE`` setting. - * ``allow_future``: A boolean specifying whether to include "future" - objects on this page, where "future" means objects in which the field - specified in ``date_field`` is greater than the current date/time. By - default, this is ``False``. +``allow_future`` + A boolean specifying whether to include "future" objects on this page, + where "future" means objects in which the field specified in ``date_field`` + is greater than the current date/time. By default, this is ``False``. -**Template name:** +Template name +~~~~~~~~~~~~~ If ``template_name`` isn't specified, this view will use the template ``<app_label>/<model_name>_archive_day.html`` by default. -**Template context:** +Template context +~~~~~~~~~~~~~~~~ In addition to ``extra_context``, the template's context will be: - * ``day``: A ``datetime.date`` object representing the given day. +``day`` + A ``datetime.date`` object representing the given day. - * ``next_day``: A ``datetime.date`` object representing the next day. If - the next day is in the future, this will be ``None``. +``next_day`` + A ``datetime.date`` object representing the next day. If the next day is in + the future, this will be ``None``. - * ``previous_day``: A ``datetime.date`` object representing the given day. - Unlike ``next_day``, this will never be ``None``. +``previous_day`` + A ``datetime.date`` object representing the given day. Unlike ``next_day``, + this will never be ``None``. - * ``object_list``: A list of objects available for the given day. This - variable's name depends on the ``template_object_name`` parameter, which - is ``'object'`` by default. If ``template_object_name`` is ``'foo'``, - this variable's name will be ``foo_list``. +``object_list`` + A list of objects available for the given day. This variable's name depends + on the ``template_object_name`` parameter, which is ``'object'`` by + default. If ``template_object_name`` is ``'foo'``, this variable's name + will be ``foo_list``. ``django.views.generic.date_based.archive_today`` ------------------------------------------------- -**Description:** +Description +~~~~~~~~~~~ A day archive page showing all objects for *today*. This is exactly the same as ``archive_day``, except the ``year``/``month``/``day`` arguments are not used, @@ -584,95 +674,108 @@ and today's date is used instead. ``django.views.generic.date_based.object_detail`` ------------------------------------------------- -**Description:** +Description +~~~~~~~~~~~ A page representing an individual object. If the object has a date value in the future, the view will throw a 404 error by default, unless you set ``allow_future`` to ``True``. -**Required arguments:** - - * ``year``: The object's four-digit year (a string). +Required arguments +~~~~~~~~~~~~~~~~~~ - * ``month``: The object's month , formatted according to the - ``month_format`` argument. +``year`` + The object's four-digit year (a string). - * ``day``: The object's day , formatted according to the ``day_format`` - argument. +``month`` + The object's month , formatted according to the ``month_format`` argument. - * ``queryset``: A ``QuerySet`` that contains the object. +``day`` + The object's day , formatted according to the ``day_format`` argument. - * ``date_field``: The name of the ``DateField`` or ``DateTimeField`` in - the ``QuerySet``'s model that the generic view should use to look up the - object according to ``year``, ``month`` and ``day``. +``queryset`` + A ``QuerySet`` that contains the object. - * Either ``object_id`` or (``slug`` *and* ``slug_field``) is required. +``date_field`` + The name of the ``DateField`` or ``DateTimeField`` in the ``QuerySet``'s + model that the generic view should use to look up the object according to + ``year``, ``month`` and ``day``. - If you provide ``object_id``, it should be the value of the primary-key - field for the object being displayed on this page. +Either ``object_id`` or (``slug`` *and* ``slug_field``) is required. + If you provide ``object_id``, it should be the value of the primary-key + field for the object being displayed on this page. - 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. By default, ``slug_field`` is ``'slug'``. + 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. By default, ``slug_field`` is ``'slug'``. -**Optional arguments:** +Optional arguments +~~~~~~~~~~~~~~~~~~ - * ``month_format``: A format string that regulates what format the - ``month`` parameter uses. This should be in the syntax accepted by - Python's ``time.strftime``. (See the `strftime docs`_.) It's set to - ``"%b"`` by default, which is a three-letter month abbreviation. To - change it to use numbers, use ``"%m"``. +``month_format`` + A format string that regulates what format the ``month`` parameter uses. + This should be in the syntax accepted by Python's ``time.strftime``. (See + the `strftime docs`_.) It's set to ``"%b"`` by default, which is a three- + letter month abbreviation. To change it to use numbers, use ``"%m"``. - * ``day_format``: Like ``month_format``, but for the ``day`` parameter. - It defaults to ``"%d"`` (day of the month as a decimal number, 01-31). +``day_format`` + Like ``month_format``, but for the ``day`` parameter. It defaults to + ``"%d"`` (day of the month as a decimal number, 01-31). - * ``template_name``: The full name of a template to use in rendering the - page. This lets you override the default template name (see below). +``template_name`` + The full name of a template to use in rendering the page. This lets you + override the default template name (see below). - * ``template_name_field``: The name of a field on the object whose value is - the template name to use. This lets you store template names in the data. - In other words, if your object has a field ``'the_template'`` that - contains a string ``'foo.html'``, and you set ``template_name_field`` to - ``'the_template'``, then the generic view for this object will use the - template ``'foo.html'``. +``template_name_field`` + The name of a field on the object whose value is the template name to use. + This lets you store template names in the data. In other words, if your + object has a field ``'the_template'`` that contains a string + ``'foo.html'``, and you set ``template_name_field`` to ``'the_template'``, + then the generic view for this object will use the template ``'foo.html'``. - It's a bit of a brain-bender, but it's useful in some cases. + It's a bit of a brain-bender, but it's useful in some cases. - * ``template_loader``: The template loader to use when loading the - template. By default, it's ``django.template.loader``. +``template_loader`` + The template loader to use when loading the template. By default, it's + ``django.template.loader``. - * ``extra_context``: A dictionary of values to add to the template - context. By default, this is an empty dictionary. If a value in the - dictionary is callable, the generic view will call it - just before rendering the template. +``extra_context`` + A dictionary of values to add to the template context. By default, this is + an empty dictionary. If a value in the dictionary is callable, the generic + view will call it just before rendering the template. - * ``context_processors``: A list of template-context processors to apply to - the view's template. See the `RequestContext docs`_. +``context_processors`` + A list of template-context processors to apply to the view's template. See + the `RequestContext docs`_. - * ``template_object_name``: Designates the name of the template variable - to use in the template context. By default, this is ``'object'``. +``template_object_name`` + Designates the name of the template variable to use in the template + context. By default, this is ``'object'``. - * ``mimetype``: The MIME type to use for the resulting document. Defaults - to the value of the ``DEFAULT_CONTENT_TYPE`` setting. +``mimetype`` + The MIME type to use for the resulting document. Defaults to the value of + the ``DEFAULT_CONTENT_TYPE`` setting. - * ``allow_future``: A boolean specifying whether to include "future" - objects on this page, where "future" means objects in which the field - specified in ``date_field`` is greater than the current date/time. By - default, this is ``False``. +``allow_future`` + A boolean specifying whether to include "future" objects on this page, + where "future" means objects in which the field specified in ``date_field`` + is greater than the current date/time. By default, this is ``False``. -**Template name:** +Template name +~~~~~~~~~~~~~ If ``template_name`` isn't specified, this view will use the template ``<app_label>/<model_name>_detail.html`` by default. -**Template context:** +Template context +~~~~~~~~~~~~~~~~ In addition to ``extra_context``, the template's context will be: - * ``object``: The object. This variable's name depends on the - ``template_object_name`` parameter, which is ``'object'`` by default. If - ``template_object_name`` is ``'foo'``, this variable's name will be - ``foo``. +``object`` + The object. This variable's name depends on the ``template_object_name`` + parameter, which is ``'object'`` by default. If ``template_object_name`` is + ``'foo'``, this variable's name will be ``foo``. List/detail generic views ========================= @@ -685,113 +788,95 @@ object page. ``django.views.generic.list_detail.object_list`` ------------------------------------------------ -**Description:** +Description +~~~~~~~~~~~ A page representing a list of objects. -**Required arguments:** +Required arguments +~~~~~~~~~~~~~~~~~~ - * ``queryset``: A ``QuerySet`` that represents the objects. +``queryset`` + A ``QuerySet`` that represents the objects. -**Optional arguments:** +Optional arguments +~~~~~~~~~~~~~~~~~~ - * ``paginate_by``: An integer specifying how many objects should be - displayed per page. If this is given, the view will paginate objects with - ``paginate_by`` objects per page. The view will expect either a ``page`` - query string parameter (via ``GET``) or a ``page`` variable specified in - the URLconf. See `Notes on pagination`_ below. +``paginate_by`` + An integer specifying how many objects should be displayed per page. If + this is given, the view will paginate objects with ``paginate_by`` objects + per page. The view will expect either a ``page`` query string parameter + (via ``GET``) or a ``page`` variable specified in the URLconf. See `Notes + on pagination`_ below. - * ``page``: The current page number, as an integer. This is 1-based. - See `Notes on pagination`_ below. +``page`` + The current (1-based) page number, as an integer, or the string ``'last'``. + See `Notes on pagination`_ below. - * ``template_name``: The full name of a template to use in rendering the - page. This lets you override the default template name (see below). +``template_name`` + The full name of a template to use in rendering the page. This lets you + override the default template name (see below). - * ``template_loader``: The template loader to use when loading the - template. By default, it's ``django.template.loader``. +``template_loader`` + The template loader to use when loading the template. By default, it's + ``django.template.loader``. - * ``extra_context``: A dictionary of values to add to the template - context. By default, this is an empty dictionary. If a value in the - dictionary is callable, the generic view will call it - just before rendering the template. +``extra_context`` + A dictionary of values to add to the template context. By default, this is + an empty dictionary. If a value in the dictionary is callable, the generic + view will call it just before rendering the template. - * ``allow_empty``: A boolean specifying whether to display the page if no - objects are available. If this is ``False`` and no objects are available, - the view will raise a 404 instead of displaying an empty page. By - default, this is ``True``. +``allow_empty`` + A boolean specifying whether to display the page if no objects are + available. If this is ``False`` and no objects are available, the view will + raise a 404 instead of displaying an empty page. By default, this is + ``True``. - * ``context_processors``: A list of template-context processors to apply to - the view's template. See the `RequestContext docs`_. +``context_processors`` + A list of template-context processors to apply to the view's template. See + the `RequestContext docs`_. - * ``template_object_name``: Designates the name of the template variable - to use in the template context. By default, this is ``'object'``. The - view will append ``'_list'`` to the value of this parameter in - determining the variable's name. +``template_object_name`` + Designates the name of the template variable to use in the template + context. By default, this is ``'object'``. The view will append ``'_list'`` + to the value of this parameter in determining the variable's name. - * ``mimetype``: The MIME type to use for the resulting document. Defaults - to the value of the ``DEFAULT_CONTENT_TYPE`` setting. +``mimetype`` + The MIME type to use for the resulting document. Defaults to the value of + the ``DEFAULT_CONTENT_TYPE`` setting. -**Template name:** +Template name +~~~~~~~~~~~~~ If ``template_name`` isn't specified, this view will use the template ``<app_label>/<model_name>_list.html`` by default. -**Template context:** +Template context +~~~~~~~~~~~~~~~~ In addition to ``extra_context``, the template's context will be: - * ``object_list``: The list of objects. This variable's name depends on the - ``template_object_name`` parameter, which is ``'object'`` by default. If - ``template_object_name`` is ``'foo'``, this variable's name will be - ``foo_list``. +``object_list`` + The list of objects. This variable's name depends on the + ``template_object_name`` parameter, which is ``'object'`` by default. If + ``template_object_name`` is ``'foo'``, this variable's name will be + ``foo_list``. - * ``is_paginated``: A boolean representing whether the results are - paginated. Specifically, this is set to ``False`` if the number of - available objects is less than or equal to ``paginate_by``. +``is_paginated`` + A boolean representing whether the results are paginated. Specifically, + this is set to ``False`` if the number of available objects is less than or + equal to ``paginate_by``. If the results are paginated, the context will contain these extra variables: - * **New in Django development version:** ``paginator``: An instance of - ``django.core.paginator.Paginator``. - - * **New in Django development version:** ``page_obj``: An instance of - ``django.core.paginator.Page``. - -In older versions of Django, before ``paginator`` and ``page_obj`` were added -to this template's context, the template included several other variables -related to pagination. Note that you should *NOT* use these variables anymore; -use ``paginator`` and ``page_obj`` instead, because they let you do everything -these old variables let you do (and more!). But for legacy installations, -here's a list of those old template variables: - - * ``results_per_page``: The number of objects per page. (Same as the - ``paginate_by`` parameter.) - - * ``has_next``: A boolean representing whether there's a next page. - - * ``has_previous``: A boolean representing whether there's a previous page. - - * ``page``: The current page number, as an integer. This is 1-based. +``paginator`` (**New in Django development version**) + An instance of ``django.core.paginator.Paginator``. - * ``next``: The next page number, as an integer. If there's no next page, - this will still be an integer representing the theoretical next-page - number. This is 1-based. +``page_obj`` (**New in Django development version**) + An instance of ``django.core.paginator.Page``. - * ``previous``: The previous page number, as an integer. This is 1-based. - - * ``last_on_page``: The number of the - last result on the current page. This is 1-based. - - * ``first_on_page``: The number of the - first result on the current page. This is 1-based. - - * ``pages``: The total number of pages, as an integer. - - * ``hits``: The total number of objects across *all* pages, not just this - page. - - * ``page_range``: A list of the page numbers that are available. This is - 1-based. +See the `pagination documentation`_ for more information on the ``Paginator`` +and ``Page`` objects. Notes on pagination ~~~~~~~~~~~~~~~~~~~ @@ -817,7 +902,7 @@ These values and lists are 1-based, not 0-based, so the first page would be represented as page ``1``. For more on pagination, read the `pagination documentation`_. - + .. _`pagination documentation`: ../pagination/ **New in Django development version:** @@ -838,67 +923,77 @@ any other value for ``page`` will result in a 404 error. A page representing an individual object. -**Description:** +Description +~~~~~~~~~~~ A page representing an individual object. -**Required arguments:** - - * ``queryset``: A ``QuerySet`` that contains the object. +Required arguments +~~~~~~~~~~~~~~~~~~ - * Either ``object_id`` or (``slug`` *and* ``slug_field``) is required. +``queryset`` + A ``QuerySet`` that contains the object. - If you provide ``object_id``, it should be the value of the primary-key - field for the object being displayed on this page. +Either ``object_id`` or (``slug`` *and* ``slug_field``) + If you provide ``object_id``, it should be the value of the primary-key + field for the object being displayed on this page. - 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. By default, ``slug_field`` is ``'slug'``. + 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. By default, ``slug_field`` is ``'slug'``. -**Optional arguments:** +Optional arguments +~~~~~~~~~~~~~~~~~~ - * ``template_name``: The full name of a template to use in rendering the - page. This lets you override the default template name (see below). +``template_name`` + The full name of a template to use in rendering the page. This lets you + override the default template name (see below). - * ``template_name_field``: The name of a field on the object whose value is - the template name to use. This lets you store template names in the data. - In other words, if your object has a field ``'the_template'`` that - contains a string ``'foo.html'``, and you set ``template_name_field`` to - ``'the_template'``, then the generic view for this object will use the - template ``'foo.html'``. +``template_name_field`` + The name of a field on the object whose value is the template name to use. + This lets you store template names in the data. In other words, if your + object has a field ``'the_template'`` that contains a string + ``'foo.html'``, and you set ``template_name_field`` to ``'the_template'``, + then the generic view for this object will use the template ``'foo.html'``. - It's a bit of a brain-bender, but it's useful in some cases. + It's a bit of a brain-bender, but it's useful in some cases. - * ``template_loader``: The template loader to use when loading the - template. By default, it's ``django.template.loader``. +``template_loader`` + The template loader to use when loading the template. By default, it's + ``django.template.loader``. - * ``extra_context``: A dictionary of values to add to the template - context. By default, this is an empty dictionary. If a value in the - dictionary is callable, the generic view will call it - just before rendering the template. +``extra_context`` + A dictionary of values to add to the template context. By default, this is + an empty dictionary. If a value in the dictionary is callable, the generic + view will call it just before rendering the template. - * ``context_processors``: A list of template-context processors to apply to - the view's template. See the `RequestContext docs`_. +``context_processors`` + A list of template-context processors to apply to the view's template. See + the `RequestContext docs`_. - * ``template_object_name``: Designates the name of the template variable - to use in the template context. By default, this is ``'object'``. +``template_object_name`` + Designates the name of the template variable to use in the template + context. By default, this is ``'object'``. - * ``mimetype``: The MIME type to use for the resulting document. Defaults - to the value of the ``DEFAULT_CONTENT_TYPE`` setting. +``mimetype`` + The MIME type to use for the resulting document. Defaults to the value of + the ``DEFAULT_CONTENT_TYPE`` setting. -**Template name:** +Template name +~~~~~~~~~~~~~ If ``template_name`` isn't specified, this view will use the template ``<app_label>/<model_name>_detail.html`` by default. -**Template context:** +Template context +~~~~~~~~~~~~~~~~ In addition to ``extra_context``, the template's context will be: - * ``object``: The object. This variable's name depends on the - ``template_object_name`` parameter, which is ``'object'`` by default. If - ``template_object_name`` is ``'foo'``, this variable's name will be - ``foo``. +``object`` + The object. This variable's name depends on the ``template_object_name`` + parameter, which is ``'object'`` by default. If ``template_object_name`` is + ``'foo'``, this variable's name will be ``foo``. Create/update/delete generic views ================================== @@ -909,242 +1004,270 @@ for creating, editing and deleting objects. **Changed in Django development version:** ``django.views.generic.create_update.create_object`` and -``django.views.generic.create_update.update_object`` now use `newforms`_ to -build and display the form. +``django.views.generic.create_update.update_object`` now use the new `forms +library`_ to build and display the form. -.. _newforms: ../newforms/ +.. _forms library: ../forms/ ``django.views.generic.create_update.create_object`` ---------------------------------------------------- -**Description:** +Description +~~~~~~~~~~~ A page that displays a form for creating an object, redisplaying the form with validation errors (if there are any) and saving the object. -**Required arguments:** - - * Either ``form_class`` or ``model`` is required. +Required arguments +~~~~~~~~~~~~~~~~~~ - If you provide ``form_class``, it should be a - ``django.newforms.ModelForm`` subclass. Use this argument when you need - to customize the model's form. See the `ModelForm docs`_ for more - information. +Either ``form_class`` or ``model`` + If you provide ``form_class``, it should be a ``django.forms.ModelForm`` + subclass. Use this argument when you need to customize the model's form. + See the `ModelForm docs`_ for more information. - Otherwise, ``model`` should be a Django model class and the form used - will be a standard ``ModelForm`` for ``model``. + Otherwise, ``model`` should be a Django model class and the form used will + be a standard ``ModelForm`` for ``model``. -**Optional arguments:** +Optional arguments +~~~~~~~~~~~~~~~~~~ - * ``post_save_redirect``: A URL to which the view will redirect after - saving the object. By default, it's ``object.get_absolute_url()``. +``post_save_redirect`` + A URL to which the view will redirect after saving the object. By default, + it's ``object.get_absolute_url()``. - ``post_save_redirect`` may contain dictionary string formatting, which - will be interpolated against the object's field attributes. For example, - you could use ``post_save_redirect="/polls/%(slug)s/"``. + ``post_save_redirect`` may contain dictionary string formatting, which will + be interpolated against the object's field attributes. For example, you + could use ``post_save_redirect="/polls/%(slug)s/"``. - * ``login_required``: A boolean that designates whether a user must be - logged in, in order to see the page and save changes. This hooks into the - Django `authentication system`_. By default, this is ``False``. +``login_required`` + A boolean that designates whether a user must be logged in, in order to see + the page and save changes. This hooks into the Django `authentication + system`_. By default, this is ``False``. - If this is ``True``, and a non-logged-in user attempts to visit this page - or save the form, Django will redirect the request to ``/accounts/login/``. + If this is ``True``, and a non-logged-in user attempts to visit this page + or save the form, Django will redirect the request to ``/accounts/login/``. - * ``template_name``: The full name of a template to use in rendering the - page. This lets you override the default template name (see below). +``template_name`` + The full name of a template to use in rendering the page. This lets you + override the default template name (see below). - * ``template_loader``: The template loader to use when loading the - template. By default, it's ``django.template.loader``. +``template_loader`` + The template loader to use when loading the template. By default, it's + ``django.template.loader``. - * ``extra_context``: A dictionary of values to add to the template - context. By default, this is an empty dictionary. If a value in the - dictionary is callable, the generic view will call it - just before rendering the template. +``extra_context`` + A dictionary of values to add to the template context. By default, this is + an empty dictionary. If a value in the dictionary is callable, the generic + view will call it just before rendering the template. - * ``context_processors``: A list of template-context processors to apply to - the view's template. See the `RequestContext docs`_. +``context_processors`` + A list of template-context processors to apply to the view's template. See + the `RequestContext docs`_. -**Template name:** +Template name +~~~~~~~~~~~~~ If ``template_name`` isn't specified, this view will use the template ``<app_label>/<model_name>_form.html`` by default. -**Template context:** +Template context +~~~~~~~~~~~~~~~~ In addition to ``extra_context``, the template's context will be: - * ``form``: A ``django.newforms.ModelForm`` instance representing the form - for creating the object. This lets you refer to form fields easily in the - template system. +``form`` + A ``django.forms.ModelForm`` instance representing the form for creating + the object. This lets you refer to form fields easily in the template + system. - For example, if the model has two fields, ``name`` and ``address``:: + For example, if the model has two fields, ``name`` and ``address``:: - <form action="" method="post"> - <p>{{ form.name.label_tag }} {{ form.name }}</p> - <p>{{ form.address.label_tag }} {{ form.address }}</p> - </form> + <form action="" method="post"> + <p>{{ form.name.label_tag }} {{ form.name }}</p> + <p>{{ form.address.label_tag }} {{ form.address }}</p> + </form> - See the `newforms documentation`_ for more information about using - ``Form`` objects in templates. + See the `forms documentation`_ for more information about using ``Form`` + objects in templates. .. _authentication system: ../authentication/ -.. _ModelForm docs: ../newforms/modelforms -.. _newforms documentation: ../newforms/ +.. _ModelForm docs: ../modelforms/ +.. _forms documentation: ../forms/ ``django.views.generic.create_update.update_object`` ---------------------------------------------------- -**Description:** +Description +~~~~~~~~~~~ A page that displays a form for editing an existing object, redisplaying the form with validation errors (if there are any) and saving changes to the -object. This uses the automatic manipulators that come with Django models. - -**Required arguments:** +object. This uses a form automatically generated from the object's +model class. - * Either ``form_class`` or ``model`` is required. +Required arguments +~~~~~~~~~~~~~~~~~~ - If you provide ``form_class``, it should be a - ``django.newforms.ModelForm`` subclass. Use this argument when you need - to customize the model's form. See the `ModelForm docs`_ for more - information. +Either ``form_class`` or ``model`` + If you provide ``form_class``, it should be a ``django.forms.ModelForm`` + subclass. Use this argument when you need to customize the model's form. + See the `ModelForm docs`_ for more information. - Otherwise, ``model`` should be a Django model class and the form used - will be a standard ``ModelForm`` for ``model``. + Otherwise, ``model`` should be a Django model class and the form used will + be a standard ``ModelForm`` for ``model``. - * Either ``object_id`` or (``slug`` *and* ``slug_field``) is required. +Either ``object_id`` or (``slug`` *and* ``slug_field``) + If you provide ``object_id``, it should be the value of the primary-key + field for the object being displayed on this page. - If you provide ``object_id``, it should be the value of the primary-key - field for the object being displayed on this page. + 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. By default, ``slug_field`` is ``'slug'``. - 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. By default, ``slug_field`` is ``'slug'``. +Optional arguments +~~~~~~~~~~~~~~~~~~ -**Optional arguments:** +``post_save_redirect`` + A URL to which the view will redirect after saving the object. By default, + it's ``object.get_absolute_url()``. - * ``post_save_redirect``: A URL to which the view will redirect after - saving the object. By default, it's ``object.get_absolute_url()``. + ``post_save_redirect`` may contain dictionary string formatting, which will + be interpolated against the object's field attributes. For example, you + could use ``post_save_redirect="/polls/%(slug)s/"``. - ``post_save_redirect`` may contain dictionary string formatting, which - will be interpolated against the object's field attributes. For example, - you could use ``post_save_redirect="/polls/%(slug)s/"``. +``login_required`` + A boolean that designates whether a user must be logged in, in order to see + the page and save changes. This hooks into the Django `authentication + system`_. By default, this is ``False``. - * ``login_required``: A boolean that designates whether a user must be - logged in, in order to see the page and save changes. This hooks into the - Django `authentication system`_. By default, this is ``False``. + If this is ``True``, and a non-logged-in user attempts to visit this page + or save the form, Django will redirect the request to ``/accounts/login/``. - If this is ``True``, and a non-logged-in user attempts to visit this page - or save the form, Django will redirect the request to ``/accounts/login/``. +``template_name`` + The full name of a template to use in rendering the page. This lets you + override the default template name (see below). - * ``template_name``: The full name of a template to use in rendering the - page. This lets you override the default template name (see below). +``template_loader`` + The template loader to use when loading the template. By default, it's + ``django.template.loader``. - * ``template_loader``: The template loader to use when loading the - template. By default, it's ``django.template.loader``. +``extra_context`` + A dictionary of values to add to the template context. By default, this is + an empty dictionary. If a value in the dictionary is callable, the generic + view will call it just before rendering the template. - * ``extra_context``: A dictionary of values to add to the template - context. By default, this is an empty dictionary. If a value in the - dictionary is callable, the generic view will call it - just before rendering the template. - - * ``context_processors``: A list of template-context processors to apply to +``context_processors``: A list of template-context processors to apply to the view's template. See the `RequestContext docs`_. - * ``template_object_name``: Designates the name of the template variable - to use in the template context. By default, this is ``'object'``. +``template_object_name`` + Designates the name of the template variable to use in the template + context. By default, this is ``'object'``. -**Template name:** +Template name +~~~~~~~~~~~~~ If ``template_name`` isn't specified, this view will use the template ``<app_label>/<model_name>_form.html`` by default. -**Template context:** +Template context +~~~~~~~~~~~~~~~~ In addition to ``extra_context``, the template's context will be: - * ``form``: A ``django.newforms.ModelForm`` instance representing the form - for editing the object. This lets you refer to form fields easily in the - template system. +``form`` + A ``django.forms.ModelForm`` instance representing the form for editing the + object. This lets you refer to form fields easily in the template system. - For example, if the model has two fields, ``name`` and ``address``:: + For example, if the model has two fields, ``name`` and ``address``:: - <form action="" method="post"> - <p>{{ form.name.label_tag }} {{ form.name }}</p> - <p>{{ form.address.label_tag }} {{ form.address }}</p> - </form> + <form action="" method="post"> + <p>{{ form.name.label_tag }} {{ form.name }}</p> + <p>{{ form.address.label_tag }} {{ form.address }}</p> + </form> - See the `newforms documentation`_ for more information about using - ``Form`` objects in templates. + See the `forms documentation`_ for more information about using ``Form`` + objects in templates. - * ``object``: The original object being edited. This variable's name - depends on the ``template_object_name`` parameter, which is ``'object'`` - by default. If ``template_object_name`` is ``'foo'``, this variable's - name will be ``foo``. +``object`` + The original object being edited. This variable's name depends on the + ``template_object_name`` parameter, which is ``'object'`` by default. If + ``template_object_name`` is ``'foo'``, this variable's name will be + ``foo``. ``django.views.generic.create_update.delete_object`` ---------------------------------------------------- -**Description:** +Description +~~~~~~~~~~~ A view that displays a confirmation page and deletes an existing object. The given object will only be deleted if the request method is ``POST``. If this view is fetched via ``GET``, it will display a confirmation page that should contain a form that POSTs to the same URL. -**Required arguments:** - - * ``model``: The Django model class of the object that the form will - create. +Required arguments +~~~~~~~~~~~~~~~~~~ - * Either ``object_id`` or (``slug`` *and* ``slug_field``) is required. +``model`` + The Django model class of the object that the form will create. - If you provide ``object_id``, it should be the value of the primary-key - field for the object being displayed on this page. +Either ``object_id`` or (``slug`` *and* ``slug_field``) + If you provide ``object_id``, it should be the value of the primary-key + field for the object being displayed on this page. - 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. By default, ``slug_field`` is ``'slug'``. + 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. By default, ``slug_field`` is ``'slug'``. - * ``post_delete_redirect``: A URL to which the view will redirect after - deleting the object. +``post_delete_redirect`` + A URL to which the view will redirect after deleting the object. -**Optional arguments:** +Optional arguments +~~~~~~~~~~~~~~~~~~ - * ``login_required``: A boolean that designates whether a user must be - logged in, in order to see the page and save changes. This hooks into the - Django `authentication system`_. By default, this is ``False``. +``login_required`` + A boolean that designates whether a user must be logged in, in order to see + the page and save changes. This hooks into the Django `authentication + system`_. By default, this is ``False``. - If this is ``True``, and a non-logged-in user attempts to visit this page - or save the form, Django will redirect the request to ``/accounts/login/``. + If this is ``True``, and a non-logged-in user attempts to visit this page + or save the form, Django will redirect the request to ``/accounts/login/``. - * ``template_name``: The full name of a template to use in rendering the - page. This lets you override the default template name (see below). +``template_name`` + The full name of a template to use in rendering the page. This lets you + override the default template name (see below). - * ``template_loader``: The template loader to use when loading the - template. By default, it's ``django.template.loader``. +``template_loader`` + The template loader to use when loading the template. By default, it's + ``django.template.loader``. - * ``extra_context``: A dictionary of values to add to the template - context. By default, this is an empty dictionary. If a value in the - dictionary is callable, the generic view will call it - just before rendering the template. +``extra_context`` + A dictionary of values to add to the template context. By default, this is + an empty dictionary. If a value in the dictionary is callable, the generic + view will call it just before rendering the template. - * ``context_processors``: A list of template-context processors to apply to - the view's template. See the `RequestContext docs`_. +``context_processors`` + A list of template-context processors to apply to the view's template. See + the `RequestContext docs`_. - * ``template_object_name``: Designates the name of the template variable - to use in the template context. By default, this is ``'object'``. +``template_object_name`` + Designates the name of the template variable to use in the template + context. By default, this is ``'object'``. -**Template name:** +Template name +~~~~~~~~~~~~~ If ``template_name`` isn't specified, this view will use the template ``<app_label>/<model_name>_confirm_delete.html`` by default. -**Template context:** +Template context +~~~~~~~~~~~~~~~~ In addition to ``extra_context``, the template's context will be: - * ``object``: The original object that's about to be deleted. This - variable's name depends on the ``template_object_name`` parameter, which - is ``'object'`` by default. If ``template_object_name`` is ``'foo'``, - this variable's name will be ``foo``. +``object`` + The original object that's about to be deleted. This variable's name + depends on the ``template_object_name`` parameter, which is ``'object'`` by + default. If ``template_object_name`` is ``'foo'``, this variable's name + will be ``foo``. diff --git a/docs/i18n.txt b/docs/i18n.txt index c221b43718..5269024e96 100644 --- a/docs/i18n.txt +++ b/docs/i18n.txt @@ -434,6 +434,12 @@ do the same, but the location of the locale directory is ``locale/LANG/LC_MESSAG message file (``conf/locale/en/LC_MESSAGES/django.po``) and use it as a starting point; it's just an empty translation file. +.. admonition:: Working on Windows? + + If you're using Windows and need to install the GNU gettext utilites so + ``django-admin makemessages`` works see `gettext on Windows`_ for more + information. + The format of ``.po`` files is straightforward. Each ``.po`` file contains a small bit of metadata, such as the translation maintainer's contact information, but the bulk of the file is a list of **messages** -- simple @@ -513,6 +519,12 @@ That's it. Your translations are ready for use. .. _Submitting and maintaining translations: ../contributing/ +.. admonition:: Working on Windows? + + If you're using Windows and need to install the GNU gettext utilites so + ``django-admin compilemessages`` works see `gettext on Windows`_ for more + information. + 3. How Django discovers language preference =========================================== @@ -888,10 +900,31 @@ does translation: * The string domain is ``django`` or ``djangojs``. This 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 + ``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``. This is mostly for convenience. +``gettext`` on Windows +====================== + +This is only needed for people who either want to extract message IDs or +compile ``.po`` files. Translation work itself just involves editing existing +``.po`` files, but if you want to create your own .po files, or want to test +or compile a changed ``.po`` file, you will need the ``gettext`` utilities: + + * Download the following zip files from http://sourceforge.net/projects/gettext + + * ``gettext-runtime-X.bin.woe32.zip`` + * ``gettext-tools-X.bin.woe32.zip`` + * ``libiconv-X.bin.woe32.zip`` + + * Extract the 3 files in the same folder (i.e. ``C:\Program Files\gettext-utils``) + + * Update the system PATH: + + * ``Control Panel > System > Advanced > Environment Variables`` + * In the ``System variables`` list, click ``Path``, click ``Edit`` + * Add ``;C:\Program Files\gettext-utils\bin`` at the end of the ``Variable value`` diff --git a/docs/index.txt b/docs/index.txt index 385ada455c..36a0273819 100644 --- a/docs/index.txt +++ b/docs/index.txt @@ -33,7 +33,7 @@ Reference transactions templates templates_python - newforms + forms modelforms testing sessions diff --git a/docs/install.txt b/docs/install.txt index bfe85fff8a..cd21bd7ec1 100644 --- a/docs/install.txt +++ b/docs/install.txt @@ -18,7 +18,7 @@ Install Apache and mod_python ============================= If you just want to experiment with Django, skip ahead to the next -section; Django includes a lightweight web server you can use for +section; Django includes a lightweight Web server you can use for testing, so you won't need to set up Apache until you're ready to deploy Django in production. @@ -64,7 +64,7 @@ installed. You will also want to read the database-specific notes for the `MySQL backend`_. * If you're using SQLite and either Python 2.3 or Python 2.4, you'll need - pysqlite_. Use version 2.0.3 or higher. Python 2.5 ships with an sqlite + pysqlite_. Use version 2.0.3 or higher. Python 2.5 ships with an SQLite wrapper in the standard library, so you don't need to install anything extra in that case. diff --git a/docs/localflavor.txt b/docs/localflavor.txt index 5a2e5b8fda..71e353c5d8 100644 --- a/docs/localflavor.txt +++ b/docs/localflavor.txt @@ -11,7 +11,7 @@ Inside that package, country- or culture-specific code is organized into subpackages, named using `ISO 3166 country codes`_. Most of the ``localflavor`` add-ons are localized form components deriving from -the newforms_ framework -- for example, a ``USStateField`` that knows how to +the forms_ framework -- for example, a ``USStateField`` that knows how to validate U.S. state abbreviations, and a ``FISocialSecurityNumber`` that knows how to validate Finnish social security numbers. @@ -19,7 +19,7 @@ To use one of these localized components, just import the relevant subpackage. For example, here's how you can create a form with a field representing a French telephone number:: - from django import newforms as forms + from django import forms from django.contrib.localflavor import fr class MyForm(forms.Form): @@ -32,6 +32,7 @@ Countries currently supported by ``localflavor`` are: * Argentina_ * Australia_ + * Austria_ * Brazil_ * Canada_ * Chile_ @@ -47,6 +48,7 @@ Countries currently supported by ``localflavor`` are: * Norway_ * Peru_ * Poland_ + * Romania_ * Slovakia_ * `South Africa`_ * Spain_ @@ -57,10 +59,10 @@ Countries currently supported by ``localflavor`` are: The ``localflavor`` package also includes a ``generic`` subpackage, containing useful code that is not specific to one particular country or culture. Currently, it defines date and datetime input fields based on those from -newforms_, but with non-US default formats. Here's an example of how to use +forms_, but with non-US default formats. Here's an example of how to use them:: - from django import newforms as forms + from django import forms from django.contrib.localflavor import generic class MyForm(forms.Form): @@ -69,6 +71,7 @@ them:: .. _ISO 3166 country codes: http://www.iso.org/iso/country_codes/iso_3166_code_lists/english_country_names_and_code_elements.htm .. _Argentina: `Argentina (django.contrib.localflavor.ar)`_ .. _Australia: `Australia (django.contrib.localflavor.au)`_ +.. _Austria: `Austria (django.contrib.localflavor.at)`_ .. _Brazil: `Brazil (django.contrib.localflavor.br)`_ .. _Canada: `Canada (django.contrib.localflavor.ca)`_ .. _Chile: `Chile (django.contrib.localflavor.cl)`_ @@ -84,13 +87,14 @@ them:: .. _Norway: `Norway (django.contrib.localflavor.no)`_ .. _Peru: `Peru (django.contrib.localflavor.pe)`_ .. _Poland: `Poland (django.contrib.localflavor.pl)`_ +.. _Romania: `Romania (django.contrib.localflavor.ro)`_ .. _Slovakia: `Slovakia (django.contrib.localflavor.sk)`_ .. _South Africa: `South Africa (django.contrib.localflavor.za)`_ .. _Spain: `Spain (django.contrib.localflavor.es)`_ .. _Switzerland: `Switzerland (django.contrib.localflavor.ch)`_ .. _United Kingdom: `United Kingdom (django.contrib.localflavor.uk)`_ .. _United States of America: `United States of America (django.contrib.localflavor.us)`_ -.. _newforms: ../newforms/ +.. _forms: ../forms/ Adding flavors ============== @@ -151,6 +155,24 @@ AUStateSelect A ``Select`` widget that uses a list of Australian states/territories as its choices. +Austria (``django.contrib.localflavor.at``) +============================================= + +ATZipCodeField +--------------- + +A form field that validates its input as an Austrian zip code. + +ATStateSelect +------------- + +A ``Select`` widget that uses a list of Austrian states as its choices. + +ATSocialSecurityNumberField +--------------------------- + +A form field that validates its input as an Austrian social security number. + Brazil (``django.contrib.localflavor.br``) ========================================== @@ -497,6 +519,52 @@ PLVoivodeshipSelect A ``Select`` widget that uses a list of Polish voivodeships (administrative provinces) as its choices. +Romania (``django.contrib.localflavor.ro``) +============================================ + +ROCIFField +---------- + +A form field that validates Romanian fiscal identification codes (CIF). The +return value strips the leading RO, if given. + +ROCNPField +---------- + +A form field that validates Romanian personal numeric codes (CNP). + +ROCountyField +------------- + +A form field that validates its input as a Romanian county (judet) name or +abbreviation. It normalizes the input to the standard vehicle registration +abbreviation for the given county. This field will only accept names written +with diacritics; consider using ROCountySelect as an alternative. + +ROCountySelect +-------------- + +A ``Select`` widget that uses a list of Romanian counties (judete) as its +choices. + +ROIBANField +----------- + +A form field that validates its input as a Romanian International Bank +Account Number (IBAN). The valid format is ROXX-XXXX-XXXX-XXXX-XXXX-XXXX, +with or without hyphens. + +ROPhoneNumberField +------------------ + +A form field that validates Romanian phone numbers, short special numbers +excluded. + +ROPostalCodeField +----------------- + +A form field that validates Romanian postal codes. + Slovakia (``django.contrib.localflavor.sk``) ============================================ diff --git a/docs/middleware.txt b/docs/middleware.txt index a2853e2965..8f93d7185d 100644 --- a/docs/middleware.txt +++ b/docs/middleware.txt @@ -104,7 +104,7 @@ 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 +something other than 200, JavaScript files (for IE compatibitility), or responses that have the ``Content-Encoding`` header already specified. django.middleware.http.ConditionalGetMiddleware diff --git a/docs/model-api.txt b/docs/model-api.txt index 4975953b97..93b27b8c11 100644 --- a/docs/model-api.txt +++ b/docs/model-api.txt @@ -112,7 +112,7 @@ class. Django uses the field class types to determine a few things: * The widget to use in Django's admin interface, if you care to use it (e.g. ``<input type="text">``, ``<select>``). * The minimal validation requirements, used in Django's admin and in - manipulators. + automatically-generated forms. Here are all available field types: @@ -144,10 +144,6 @@ The admin represents this as an ``<input type="text">`` (a single-line input). (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 preferred. - ``CommaSeparatedIntegerField`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -641,9 +637,8 @@ callable it will be called every time a new object is created. ``editable`` ~~~~~~~~~~~~ -If ``False``, the field will not be editable in the admin or via form -processing using the object's ``AddManipulator`` or ``ChangeManipulator`` -classes. Default is ``True``. +If ``False``, the field will not be editable in the admin or via forms +automatically generated from the model class. Default is ``True``. ``help_text`` ~~~~~~~~~~~~~ @@ -656,7 +651,10 @@ 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>." + help_text="Please use the following format: <em>YYYY-MM-DD</em>." + +Alternatively you can use plain text and +``django.utils.html.escape()`` to escape any HTML special characters. ``primary_key`` ~~~~~~~~~~~~~~~ @@ -716,7 +714,7 @@ that takes the parameters ``field_data, all_data`` and raises Django comes with quite a few validators. They're in ``django.core.validators``. -.. _validator docs: ../forms/#validators +.. _validator docs: ../oldforms/#validators Verbose field names ------------------- @@ -942,6 +940,139 @@ the relationship should work. All are optional: ======================= ============================================================ +Extra fields on many-to-many relationships +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**New in Django development version** + +When you're only dealing with simple many-to-many relationships such as +mixing and matching pizzas and toppings, a standard ``ManyToManyField`` +is all you need. However, sometimes you may need to associate data with the +relationship between two models. + +For example, consider the case of an application tracking the musical groups +which musicians belong to. There is a many-to-many relationship between a person +and the groups of which they are a member, so you could use a ManyToManyField +to represent this relationship. However, there is a lot of detail about the +membership that you might want to collect, such as the date at which the person +joined the group. + +For these situations, Django allows you to specify the model that will be used +to govern the many-to-many relationship. You can then put extra fields on the +intermediate model. The intermediate model is associated with the +``ManyToManyField`` using the ``through`` argument to point to the model +that will act as an intermediary. For our musician example, the code would look +something like this:: + + class Person(models.Model): + name = models.CharField(max_length=128) + + def __unicode__(self): + return self.name + + class Group(models.Model): + name = models.CharField(max_length=128) + members = models.ManyToManyField(Person, through='Membership') + + def __unicode__(self): + return self.name + + class Membership(models.Model): + person = models.ForeignKey(Person) + group = models.ForeignKey(Group) + date_joined = models.DateField() + invite_reason = models.CharField(max_length=64) + +When you set up the intermediary model, you explicitly specify foreign +keys to the models that are involved in the ManyToMany relation. This +explicit declaration defines how the two models are related. + +There are a few restrictions on the intermediate model: + + * Your intermediate model must contain one - and *only* one - foreign key + on the target model (this would be ``Person`` in our example). If you + have more than one foreign key, a validation error will be raised. + + * Your intermediate model must contain one - and *only* one - foreign key + on the source model (this would be ``Group`` in our example). If you + have more than one foreign key, a validation error will be raised. + + * The only exception to this is a model which has a many-to-many + relationship to itself, through an intermediary model. In this + case, two foreign keys to the same model are permitted, but they + will be treated as the two (different) sides of the many-to-many + relation. + + * When defining a many-to-many relationship from a model to + itself, using an intermediary model, you *must* use + ``symmetrical=False`` (see the documentation for + ``ManyToManyField`` above). + +Now that you have set up your ``ManyToManyField`` to use your intermediary +model (Membership, in this case), you're ready to start creating some +many-to-many relationships. You do this by creating instances of the +intermediate model:: + + >>> ringo = Person.objects.create(name="Ringo Starr") + >>> paul = Person.objects.create(name="Paul McCartney") + >>> beatles = Group.objects.create(name="The Beatles") + >>> m1 = Membership(person=ringo, group=beatles, + ... date_joined=date(1962, 8, 16), + ... invite_reason= "Needed a new drummer.") + >>> m1.save() + >>> beatles.members.all() + [<Person: Ringo Starr>] + >>> ringo.group_set.all() + [<Group: The Beatles>] + >>> m2 = Membership.objects.create(person=paul, group=beatles, + ... date_joined=date(1960, 8, 1), + ... invite_reason= "Wanted to form a band.") + >>> beatles.members.all() + [<Person: Ringo Starr>, <Person: Paul McCartney>] + +Unlike normal many-to-many fields, you *can't* use ``add``, ``create``, +or assignment (i.e., ``beatles.members = [...]``) to create relationships:: + + # THIS WILL NOT WORK + >>> beatles.members.add(john) + # NEITHER WILL THIS + >>> beatles.members.create(name="George Harrison") + # AND NEITHER WILL THIS + >>> beatles.members = [john, paul, ringo, george] + +Why? You can't just create a relationship between a Person and a Group - you +need to specify all the detail for the relationship required by the +Membership table. The simple ``add``, ``create`` and assignment calls +don't provide a way to specify this extra detail. As a result, they are +disabled for many-to-many relationships that use an intermediate model. +The only way to create a many-to-many relationship with an intermediate table +is to create instances of the intermediate model. + +The ``remove`` method is disabled for similar reasons. However, the +``clear()`` method can be used to remove all many-to-many relationships +for an instance:: + + # Beatles have broken up + >>> beatles.members.clear() + +Once you have established the many-to-many relationships by creating instances +of your intermediate model, you can issue queries. Just as with normal +many-to-many relationships, you can query using the attributes of the +many-to-many-related model:: + + # Find all the groups with a member whose name starts with 'Paul' + >>> Groups.objects.filter(person__name__startswith='Paul') + [<Group: The Beatles>] + +As you are using an intermediate table, you can also query on the attributes +of the intermediate model:: + + # Find all the members of the Beatles that joined after 1 Jan 1961 + >>> Person.objects.filter( + ... group__name='The Beatles', + ... membership__date_joined__gt=date(1961,1,1)) + [<Person: Ringo Starr] + One-to-one relationships ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1143,7 +1274,7 @@ any parent classes in ``unique_together``. For convenience, unique_together can be a single list when dealing with a single set of fields:: - unique_together = ("driver", "restaurant") + unique_together = ("driver", "restaurant") ``verbose_name`` ---------------- @@ -1376,6 +1507,19 @@ good idea to be careful in your choice of default manager, in order to avoid a situation where overriding of ``get_query_set()`` results in an inability to retrieve objects you'd like to work with. +Using managers for related object access +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +By default, Django uses a "bare" (i.e. default) manager when accessing related +objects (i.e. ``choice.poll``). If this default isn't appropriate for your +default manager, you can force Django to use a custom manager for related object +attributes by giving it a ``use_for_related_fields`` property:: + + class MyManager(models.Manager):: + use_for_related_fields = True + + ... + Model methods ============= diff --git a/docs/modelforms.txt b/docs/modelforms.txt index 9c06bc409d..1be7c3a882 100644 --- a/docs/modelforms.txt +++ b/docs/modelforms.txt @@ -1,6 +1,6 @@ -========================== -Using newforms with models -========================== +======================= +Using forms with models +======================= ``ModelForm`` ============= @@ -16,7 +16,7 @@ class from a Django model. For example:: - >>> from django.newforms import ModelForm + >>> from django.forms import ModelForm # Create the form class. >>> class ArticleForm(ModelForm): @@ -86,11 +86,11 @@ the full list of conversions: As you might expect, the ``ForeignKey`` and ``ManyToManyField`` model field types are special cases: - * ``ForeignKey`` is represented by ``django.newforms.ModelChoiceField``, + * ``ForeignKey`` is represented by ``django.forms.ModelChoiceField``, which is a ``ChoiceField`` whose choices are a model ``QuerySet``. * ``ManyToManyField`` is represented by - ``django.newforms.ModelMultipleChoiceField``, which is a + ``django.forms.ModelMultipleChoiceField``, which is a ``MultipleChoiceField`` whose choices are a model ``QuerySet``. In addition, each generated form field has attributes set as follows: @@ -121,7 +121,7 @@ A full example Consider this set of models:: from django.db import models - from django.newforms import ModelForm + from django.forms import ModelForm TITLE_CHOICES = ( ('MR', 'Mr.'), @@ -240,14 +240,14 @@ For example:: >>> new_author = f.save() Other than the ``save()`` and ``save_m2m()`` methods, a ``ModelForm`` -works exactly the same way as any other ``newforms`` form. For +works exactly the same way as any other ``forms`` form. For example, the ``is_valid()`` method is used to check for validity, the ``is_multipart()`` method is used to determine whether a form requires multipart file upload (and hence whether ``request.FILES`` must be -passed to the form), etc. See `the standard newforms documentation`_ +passed to the form), etc. See `the standard forms documentation`_ for more information. -.. _the standard newforms documentation: ../newforms/ +.. _the standard forms documentation: ../forms/ Using a subset of fields on the form ------------------------------------ @@ -384,7 +384,7 @@ Similar to regular formsets there are a couple enhanced formset classes that provide all the right things to work with your models. Lets reuse the ``Author`` model from above:: - >>> from django.newforms.models import modelformset_factory + >>> from django.forms.models import modelformset_factory >>> AuthorFormSet = modelformset_factory(Author) This will create a formset that is capable of working with the data associated @@ -417,7 +417,7 @@ configurable:: Alternatively, you can use a subclassing based approach:: - from django.newforms.models import BaseModelFormSet + from django.forms.models import BaseModelFormSet class BaseAuthorFormSet(BaseModelFormSet): def get_queryset(self): @@ -494,7 +494,7 @@ with related objects through a foreign key. Suppose you have two models ``Author`` and ``Book``. You want to create a formset that works with the books of a specific author. Here is how you could accomplish this:: - >>> from django.newforms.models import inlineformset_factory + >>> from django.forms.models import inlineformset_factory >>> BookFormSet = inlineformset_factory(Author, Book) >>> author = Author.objects.get(name=u'Orson Scott Card') >>> formset = BookFormSet(instance=author) diff --git a/docs/modpython.txt b/docs/modpython.txt index aa1acf5864..44de0e1bd2 100644 --- a/docs/modpython.txt +++ b/docs/modpython.txt @@ -35,6 +35,7 @@ Then edit your ``httpd.conf`` file and add the following:: SetHandler python-program PythonHandler django.core.handlers.modpython SetEnv DJANGO_SETTINGS_MODULE mysite.settings + PythonOption django.root /mysite PythonDebug On </Location> @@ -45,6 +46,24 @@ This tells Apache: "Use mod_python for any URL at or under '/mysite/', using the Django mod_python handler." It passes the value of ``DJANGO_SETTINGS_MODULE`` so mod_python knows which settings to use. +**New in Django development version:** Because mod_python does not know we are +serving this site from underneath the ``/mysite/`` prefix, this value needs to +be passed through to the mod_python handler in Django, via the ``PythonOption +django.root ...`` line. The value set on that line (the last item) should +match the string given in the ``<Location ...>`` directive. The effect of this +is that Django will automatically strip the ``/mysite`` string from the front +of any URLs before matching them against your ``URLConf`` patterns. If you +later move your site to live under ``/mysite2``, you will not have to change +anything except the ``django.root`` option in the config file. + +When using ``django.root`` you should make sure that what's left, after the +prefix has been removed, begins with a slash. Your URLConf patterns that are +expecting an initial slash will then work correctly. In the above example, +since we want to send things like ``/mysite/admin/`` to ``/admin/``, we need +to remove the string ``/mysite`` from the beginning, so that is the +``django.root`` value. It would be an error to use ``/mysite/`` (with a +trailing slash) in this case. + Note that we're using the ``<Location>`` directive, not the ``<Directory>`` directive. The latter is used for pointing at places on your filesystem, whereas ``<Location>`` points at places in the URL structure of a Web site. @@ -59,6 +78,7 @@ computer, you'll have to tell mod_python where your project can be found: SetHandler python-program PythonHandler django.core.handlers.modpython SetEnv DJANGO_SETTINGS_MODULE mysite.settings + PythonOption django.root /mysite PythonDebug On **PythonPath "['/path/to/project'] + sys.path"** </Location> diff --git a/docs/newforms.txt b/docs/newforms.txt deleted file mode 100644 index 88b25be915..0000000000 --- a/docs/newforms.txt +++ /dev/null @@ -1,2522 +0,0 @@ -==================== -The newforms library -==================== - -``django.newforms`` is Django's fantastic new form-handling library. It's a -replacement for ``django.forms``, the old form/manipulator/validation -framework. This document explains how to use this new library. - -Migration plan -============== - -``django.newforms`` is new in Django's 0.96 release, but, as it won't be new -forever, we plan to rename it to ``django.forms`` in the future. The current -``django.forms`` package will be available as ``django.oldforms`` until Django -1.0, when we plan to remove it for good. - -That has direct repercussions on the forward compatibility of your code. Please -read the following migration plan and code accordingly: - - * The old forms framework (the current ``django.forms``) has been copied to - ``django.oldforms``. Thus, you can start upgrading your code *now*, - rather than waiting for the future backwards-incompatible change, by - changing your import statements like this:: - - from django import forms # old - from django import oldforms as forms # new - - * In the next Django release (0.97), we will move the current - ``django.newforms`` to ``django.forms``. This will be a - backwards-incompatible change, and anybody who is still using the old - version of ``django.forms`` at that time will need to change their import - statements, as described in the previous bullet. - - * We will remove ``django.oldforms`` in the release *after* the next Django - release -- either 0.98 or 1.0, whichever comes first. - -With this in mind, we recommend you use the following import statement when -using ``django.newforms``:: - - from django import newforms as forms - -This way, your code can refer to the ``forms`` module, and when -``django.newforms`` is renamed to ``django.forms``, you'll only have to change -your ``import`` statements. - -If you prefer "``import *``" syntax, you can do the following:: - - from django.newforms import * - -This will import all fields, widgets, form classes and other various utilities -into your local namespace. Some people find this convenient; others find it -too messy. The choice is yours. - -Overview -======== - -As with the ``django.forms`` ("manipulators") system before it, -``django.newforms`` is intended to handle HTML form display, data processing -(validation) and redisplay. It's what you use if you want to perform -server-side validation for an HTML form. - -For example, if your Web site has a contact form that visitors can use to -send you e-mail, you'd use this library to implement the display of the HTML -form fields, along with the form validation. Any time you need to use an HTML -``<form>``, you can use this library. - -The library deals with these concepts: - - * **Widget** -- A class that corresponds to an HTML form widget, e.g. - ``<input type="text">`` or ``<textarea>``. This handles rendering of the - widget as HTML. - - * **Field** -- A class that is responsible for doing validation, e.g. - an ``EmailField`` that makes sure its data is a valid e-mail address. - - * **Form** -- A collection of fields that knows how to validate itself and - display itself as HTML. - - * **Media** -- A definition of the CSS and JavaScript resources that are - required to render a form. - -The library is decoupled from the other Django components, such as the database -layer, views and templates. It relies only on Django settings, a couple of -``django.utils`` helper functions and Django's internationalization hooks (but -you're not required to be using internationalization features to use this -library). - -Form objects -============ - -The primary way of using the ``newforms`` library is to create a form object. -Do this by subclassing ``django.newforms.Form`` and specifying the form's -fields, in a declarative style that you'll be familiar with if you've used -Django database models. In this section, we'll iteratively develop a form -object that you might use to implement "contact me" functionality on your -personal Web site. - -Start with this basic ``Form`` subclass, which we'll call ``ContactForm``:: - - from django import newforms as forms - - class ContactForm(forms.Form): - subject = forms.CharField(max_length=100) - message = forms.CharField() - sender = forms.EmailField() - cc_myself = forms.BooleanField(required=False) - -A form is composed of ``Field`` objects. In this case, our form has four -fields: ``subject``, ``message``, ``sender`` and ``cc_myself``. We'll explain -the different types of fields -- e.g., ``CharField`` and ``EmailField`` -- -shortly. - -Creating ``Form`` instances ---------------------------- - -A ``Form`` instance is either **bound** to a set of data, or **unbound**. - - * If it's **bound** to a set of data, it's capable of validating that data - and rendering the form as HTML with the data displayed in the HTML. - - * If it's **unbound**, it cannot do validation (because there's no data to - validate!), but it can still render the blank form as HTML. - -To create an unbound ``Form`` instance, simply instantiate the class:: - - >>> f = ContactForm() - -To bind data to a form, pass the data as a dictionary as the first parameter to -your ``Form`` class constructor:: - - >>> data = {'subject': 'hello', - ... 'message': 'Hi there', - ... 'sender': 'foo@example.com', - ... 'cc_myself': True} - >>> f = ContactForm(data) - -In this dictionary, the keys are the field names, which correspond to the -attributes in your ``Form`` class. The values are the data you're trying -to validate. These will usually be strings, but there's no requirement that -they be strings; the type of data you pass depends on the ``Field``, as we'll -see in a moment. - -If you need to distinguish between bound and unbound form instances at runtime, -check the value of the form's ``is_bound`` attribute:: - - >>> f = ContactForm() - >>> f.is_bound - False - >>> f = ContactForm({'subject': 'hello'}) - >>> f.is_bound - True - -Note that passing an empty dictionary creates a *bound* form with empty data:: - - >>> f = ContactForm({}) - >>> f.is_bound - True - -If you have a bound ``Form`` instance and want to change the data somehow, or -if you want to bind an unbound ``Form`` instance to some data, create another -``Form`` instance. There is no way to change data in a ``Form`` instance. Once -a ``Form`` instance has been created, you should consider its data immutable, -whether it has data or not. - -Using forms to validate data ----------------------------- - -The primary task of a ``Form`` object is to validate data. With a bound -``Form`` instance, call the ``is_valid()`` method to run validation and return -a boolean designating whether the data was valid:: - - >>> data = {'subject': 'hello', - ... 'message': 'Hi there', - ... 'sender': 'foo@example.com', - ... 'cc_myself': True} - >>> f = ContactForm(data) - >>> f.is_valid() - True - -Let's try with some invalid data. In this case, ``subject`` is blank (an error, -because all fields are required by default) and ``sender`` is not a valid -e-mail address:: - - >>> data = {'subject': '', - ... 'message': 'Hi there', - ... 'sender': 'invalid e-mail address', - ... 'cc_myself': True} - >>> f = ContactForm(data) - >>> f.is_valid() - False - -Access the ``errors`` attribute to get a dictionary of error messages:: - - >>> f.errors - {'sender': [u'Enter a valid e-mail address.'], 'subject': [u'This field is required.']} - -In this dictionary, the keys are the field names, and the values are lists of -Unicode strings representing the error messages. The error messages are stored -in lists because a field can have multiple error messages. - -You can access ``errors`` without having to call ``is_valid()`` first. The -form's data will be validated the first time either you call ``is_valid()`` or -access ``errors``. - -The validation routines will only get called once, regardless of how many times -you access ``errors`` or call ``is_valid()``. This means that if validation has -side effects, those side effects will only be triggered once. - -Behavior of unbound forms -~~~~~~~~~~~~~~~~~~~~~~~~~ - -It's meaningless to validate a form with no data, but, for the record, here's -what happens with unbound forms:: - - >>> f = ContactForm() - >>> f.is_valid() - False - >>> f.errors - {} - -Accessing "clean" data ----------------------- - -Each ``Field`` in a ``Form`` class is responsible not only for validating data, -but also for "cleaning" it -- normalizing it to a consistent format. This is a -nice feature, because it allows data for a particular field to be input in -a variety of ways, always resulting in consistent output. - -For example, ``DateField`` normalizes input into a Python ``datetime.date`` -object. Regardless of whether you pass it a string in the format -``'1994-07-15'``, a ``datetime.date`` object or a number of other formats, -``DateField`` will always normalize it to a ``datetime.date`` object as long as -it's valid. - -Once you've created a ``Form`` instance with a set of data and validated it, -you can access the clean data via the ``cleaned_data`` attribute of the ``Form`` -object:: - - >>> data = {'subject': 'hello', - ... 'message': 'Hi there', - ... 'sender': 'foo@example.com', - ... 'cc_myself': True} - >>> f = ContactForm(data) - >>> f.is_valid() - True - >>> f.cleaned_data - {'cc_myself': True, 'message': u'Hi there', 'sender': u'foo@example.com', 'subject': u'hello'} - -.. note:: - **New in Django development version** The ``cleaned_data`` attribute was - called ``clean_data`` in earlier releases. - -Note that any text-based field -- such as ``CharField`` or ``EmailField`` -- -always cleans the input into a Unicode string. We'll cover the encoding -implications later in this document. - -If your data does *not* validate, your ``Form`` instance will not have a -``cleaned_data`` attribute:: - - >>> data = {'subject': '', - ... 'message': 'Hi there', - ... 'sender': 'invalid e-mail address', - ... 'cc_myself': True} - >>> f = ContactForm(data) - >>> f.is_valid() - False - >>> f.cleaned_data - Traceback (most recent call last): - ... - AttributeError: 'ContactForm' object has no attribute 'cleaned_data' - -``cleaned_data`` will always *only* contain a key for fields defined in the -``Form``, even if you pass extra data when you define the ``Form``. In this -example, we pass a bunch of extra fields to the ``ContactForm`` constructor, -but ``cleaned_data`` contains only the form's fields:: - - >>> data = {'subject': 'hello', - ... 'message': 'Hi there', - ... 'sender': 'foo@example.com', - ... 'cc_myself': True, - ... 'extra_field_1': 'foo', - ... 'extra_field_2': 'bar', - ... 'extra_field_3': 'baz'} - >>> f = ContactForm(data) - >>> f.is_valid() - True - >>> f.cleaned_data # Doesn't contain extra_field_1, etc. - {'cc_myself': True, 'message': u'Hi there', 'sender': u'foo@example.com', 'subject': u'hello'} - -``cleaned_data`` will include a key and value for *all* fields defined in the -``Form``, even if the data didn't include a value for fields that are not -required. In this example, the data dictionary doesn't include a value for the -``nick_name`` field, but ``cleaned_data`` includes it, with an empty value:: - - >>> class OptionalPersonForm(Form): - ... first_name = CharField() - ... last_name = CharField() - ... nick_name = CharField(required=False) - >>> data = {'first_name': u'John', 'last_name': u'Lennon'} - >>> f = OptionalPersonForm(data) - >>> f.is_valid() - True - >>> f.cleaned_data - {'nick_name': u'', 'first_name': u'John', 'last_name': u'Lennon'} - -In this above example, the ``cleaned_data`` value for ``nick_name`` is set to an -empty string, because ``nick_name`` is ``CharField``, and ``CharField``\s treat -empty values as an empty string. Each field type knows what its "blank" value -is -- e.g., for ``DateField``, it's ``None`` instead of the empty string. For -full details on each field's behavior in this case, see the "Empty value" note -for each field in the "Built-in ``Field`` classes" section below. - -You can write code to perform validation for particular form fields (based on -their name) or for the form as a whole (considering combinations of various -fields). More information about this is in the `Custom form and field -validation`_ section, below. - -Behavior of unbound forms -~~~~~~~~~~~~~~~~~~~~~~~~~ - -It's meaningless to request "cleaned" data in a form with no data, but, for the -record, here's what happens with unbound forms:: - - >>> f = ContactForm() - >>> f.cleaned_data - Traceback (most recent call last): - ... - AttributeError: 'ContactForm' object has no attribute 'cleaned_data' - -Outputting forms as HTML ------------------------- - -The second task of a ``Form`` object is to render itself as HTML. To do so, -simply ``print`` it:: - - >>> f = ContactForm() - >>> print f - <tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" /></td></tr> - <tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" /></td></tr> - <tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" /></td></tr> - <tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" /></td></tr> - -If the form is bound to data, the HTML output will include that data -appropriately. For example, if a field is represented by an -``<input type="text">``, the data will be in the ``value`` attribute. If a -field is represented by an ``<input type="checkbox">``, then that HTML will -include ``checked="checked"`` if appropriate:: - - >>> data = {'subject': 'hello', - ... 'message': 'Hi there', - ... 'sender': 'foo@example.com', - ... 'cc_myself': True} - >>> f = ContactForm(data) - >>> print f - <tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" value="hello" /></td></tr> - <tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" value="Hi there" /></td></tr> - <tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" value="foo@example.com" /></td></tr> - <tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" checked="checked" /></td></tr> - -This default output is a two-column HTML table, with a ``<tr>`` for each field. -Notice the following: - - * For flexibility, the output does *not* include the ``<table>`` and - ``</table>`` tags, nor does it include the ``<form>`` and ``</form>`` - tags or an ``<input type="submit">`` tag. It's your job to do that. - - * Each field type has a default HTML representation. ``CharField`` and - ``EmailField`` are represented by an ``<input type="text">``. - ``BooleanField`` is represented by an ``<input type="checkbox">``. Note - these are merely sensible defaults; you can specify which HTML to use for - a given field by using widgets, which we'll explain shortly. - - * The HTML ``name`` for each tag is taken directly from its attribute name - in the ``ContactForm`` class. - - * The text label for each field -- e.g. ``'Subject:'``, ``'Message:'`` and - ``'Cc myself:'`` is generated from the field name by converting all - underscores to spaces and upper-casing the first letter. Again, note - these are merely sensible defaults; you can also specify labels manually. - - * Each text label is surrounded in an HTML ``<label>`` tag, which points - to the appropriate form field via its ``id``. Its ``id``, in turn, is - generated by prepending ``'id_'`` to the field name. The ``id`` - attributes and ``<label>`` tags are included in the output by default, to - follow best practices, but you can change that behavior. - -Although ``<table>`` output is the default output style when you ``print`` a -form, other output styles are available. Each style is available as a method on -a form object, and each rendering method returns a Unicode object. - -``as_p()`` -~~~~~~~~~~ - -``Form.as_p()`` renders the form as a series of ``<p>`` tags, with each ``<p>`` -containing one field:: - - >>> f = ContactForm() - >>> f.as_p() - u'<p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" /></p>\n<p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></p>\n<p><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" /></p>\n<p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></p>' - >>> print f.as_p() - <p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" /></p> - <p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></p> - <p><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" /></p> - <p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></p> - -``as_ul()`` -~~~~~~~~~~~ - -``Form.as_ul()`` renders the form as a series of ``<li>`` tags, with each -``<li>`` containing one field. It does *not* include the ``<ul>`` or ``</ul>``, -so that you can specify any HTML attributes on the ``<ul>`` for flexibility:: - - >>> f = ContactForm() - >>> f.as_ul() - u'<li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" /></li>\n<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></li>\n<li><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" /></li>\n<li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></li>' - >>> print f.as_ul() - <li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" /></li> - <li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></li> - <li><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" /></li> - <li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></li> - -``as_table()`` -~~~~~~~~~~~~~~ - -Finally, ``Form.as_table()`` outputs the form as an HTML ``<table>``. This is -exactly the same as ``print``. In fact, when you ``print`` a form object, it -calls its ``as_table()`` method behind the scenes:: - - >>> f = ContactForm() - >>> f.as_table() - u'<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" /></td></tr>\n<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" /></td></tr>\n<tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" /></td></tr>\n<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" /></td></tr>' - >>> print f.as_table() - <tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" /></td></tr> - <tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" /></td></tr> - <tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" /></td></tr> - <tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" /></td></tr> - -Configuring HTML ``<label>`` tags -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -An HTML ``<label>`` tag designates which label text is associated with which -form element. This small enhancement makes forms more usable and more accessible -to assistive devices. It's always a good idea to use ``<label>`` tags. - -By default, the form rendering methods include HTML ``id`` attributes on the -form elements and corresponding ``<label>`` tags around the labels. The ``id`` -attribute values are generated by prepending ``id_`` to the form field names. -This behavior is configurable, though, if you want to change the ``id`` -convention or remove HTML ``id`` attributes and ``<label>`` tags entirely. - -Use the ``auto_id`` argument to the ``Form`` constructor to control the label -and ``id`` behavior. This argument must be ``True``, ``False`` or a string. - -If ``auto_id`` is ``False``, then the form output will not include ``<label>`` -tags nor ``id`` attributes:: - - >>> f = ContactForm(auto_id=False) - >>> print f.as_table() - <tr><th>Subject:</th><td><input type="text" name="subject" maxlength="100" /></td></tr> - <tr><th>Message:</th><td><input type="text" name="message" /></td></tr> - <tr><th>Sender:</th><td><input type="text" name="sender" /></td></tr> - <tr><th>Cc myself:</th><td><input type="checkbox" name="cc_myself" /></td></tr> - >>> print f.as_ul() - <li>Subject: <input type="text" name="subject" maxlength="100" /></li> - <li>Message: <input type="text" name="message" /></li> - <li>Sender: <input type="text" name="sender" /></li> - <li>Cc myself: <input type="checkbox" name="cc_myself" /></li> - >>> print f.as_p() - <p>Subject: <input type="text" name="subject" maxlength="100" /></p> - <p>Message: <input type="text" name="message" /></p> - <p>Sender: <input type="text" name="sender" /></p> - <p>Cc myself: <input type="checkbox" name="cc_myself" /></p> - -If ``auto_id`` is set to ``True``, then the form output *will* include -``<label>`` tags and will simply use the field name as its ``id`` for each form -field:: - - >>> f = ContactForm(auto_id=True) - >>> print f.as_table() - <tr><th><label for="subject">Subject:</label></th><td><input id="subject" type="text" name="subject" maxlength="100" /></td></tr> - <tr><th><label for="message">Message:</label></th><td><input type="text" name="message" id="message" /></td></tr> - <tr><th><label for="sender">Sender:</label></th><td><input type="text" name="sender" id="sender" /></td></tr> - <tr><th><label for="cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="cc_myself" /></td></tr> - >>> print f.as_ul() - <li><label for="subject">Subject:</label> <input id="subject" type="text" name="subject" maxlength="100" /></li> - <li><label for="message">Message:</label> <input type="text" name="message" id="message" /></li> - <li><label for="sender">Sender:</label> <input type="text" name="sender" id="sender" /></li> - <li><label for="cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="cc_myself" /></li> - >>> print f.as_p() - <p><label for="subject">Subject:</label> <input id="subject" type="text" name="subject" maxlength="100" /></p> - <p><label for="message">Message:</label> <input type="text" name="message" id="message" /></p> - <p><label for="sender">Sender:</label> <input type="text" name="sender" id="sender" /></p> - <p><label for="cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="cc_myself" /></p> - -If ``auto_id`` is set to a string containing the format character ``'%s'``, -then the form output will include ``<label>`` tags, and will generate ``id`` -attributes based on the format string. For example, for a format string -``'field_%s'``, a field named ``subject`` will get the ``id`` value -``'field_subject'``. Continuing our example:: - - >>> f = ContactForm(auto_id='id_for_%s') - >>> print f.as_table() - <tr><th><label for="id_for_subject">Subject:</label></th><td><input id="id_for_subject" type="text" name="subject" maxlength="100" /></td></tr> - <tr><th><label for="id_for_message">Message:</label></th><td><input type="text" name="message" id="id_for_message" /></td></tr> - <tr><th><label for="id_for_sender">Sender:</label></th><td><input type="text" name="sender" id="id_for_sender" /></td></tr> - <tr><th><label for="id_for_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></td></tr> - >>> print f.as_ul() - <li><label for="id_for_subject">Subject:</label> <input id="id_for_subject" type="text" name="subject" maxlength="100" /></li> - <li><label for="id_for_message">Message:</label> <input type="text" name="message" id="id_for_message" /></li> - <li><label for="id_for_sender">Sender:</label> <input type="text" name="sender" id="id_for_sender" /></li> - <li><label for="id_for_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></li> - >>> print f.as_p() - <p><label for="id_for_subject">Subject:</label> <input id="id_for_subject" type="text" name="subject" maxlength="100" /></p> - <p><label for="id_for_message">Message:</label> <input type="text" name="message" id="id_for_message" /></p> - <p><label for="id_for_sender">Sender:</label> <input type="text" name="sender" id="id_for_sender" /></p> - <p><label for="id_for_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></p> - -If ``auto_id`` is set to any other true value -- such as a string that doesn't -include ``%s`` -- then the library will act as if ``auto_id`` is ``True``. - -By default, ``auto_id`` is set to the string ``'id_%s'``. - -Normally, a colon (``:``) will be appended after any label name when a form is -rendered. It's possible to change the colon to another character, or omit it -entirely, using the ``label_suffix`` parameter:: - - >>> f = ContactForm(auto_id='id_for_%s', label_suffix='') - >>> print f.as_ul() - <li><label for="id_for_subject">Subject</label> <input id="id_for_subject" type="text" name="subject" maxlength="100" /></li> - <li><label for="id_for_message">Message</label> <input type="text" name="message" id="id_for_message" /></li> - <li><label for="id_for_sender">Sender</label> <input type="text" name="sender" id="id_for_sender" /></li> - <li><label for="id_for_cc_myself">Cc myself</label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></li> - >>> f = ContactForm(auto_id='id_for_%s', label_suffix=' ->') - >>> print f.as_ul() - <li><label for="id_for_subject">Subject -></label> <input id="id_for_subject" type="text" name="subject" maxlength="100" /></li> - <li><label for="id_for_message">Message -></label> <input type="text" name="message" id="id_for_message" /></li> - <li><label for="id_for_sender">Sender -></label> <input type="text" name="sender" id="id_for_sender" /></li> - <li><label for="id_for_cc_myself">Cc myself -></label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></li> - -Note that the label suffix is added only if the last character of the -label isn't a punctuation character (``.``, ``!``, ``?`` or ``:``) - -Notes on field ordering -~~~~~~~~~~~~~~~~~~~~~~~ - -In the ``as_p()``, ``as_ul()`` and ``as_table()`` shortcuts, the fields are -displayed in the order in which you define them in your form class. For -example, in the ``ContactForm`` example, the fields are defined in the order -``subject``, ``message``, ``sender``, ``cc_myself``. To reorder the HTML -output, just change the order in which those fields are listed in the class. - -How errors are displayed -~~~~~~~~~~~~~~~~~~~~~~~~ - -If you render a bound ``Form`` object, the act of rendering will automatically -run the form's validation if it hasn't already happened, and the HTML output -will include the validation errors as a ``<ul class="errorlist">`` near the -field. The particular positioning of the error messages depends on the output -method you're using:: - - >>> data = {'subject': '', - ... 'message': 'Hi there', - ... 'sender': 'invalid e-mail address', - ... 'cc_myself': True} - >>> f = ContactForm(data, auto_id=False) - >>> print f.as_table() - <tr><th>Subject:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="subject" maxlength="100" /></td></tr> - <tr><th>Message:</th><td><input type="text" name="message" value="Hi there" /></td></tr> - <tr><th>Sender:</th><td><ul class="errorlist"><li>Enter a valid e-mail address.</li></ul><input type="text" name="sender" value="invalid e-mail address" /></td></tr> - <tr><th>Cc myself:</th><td><input checked="checked" type="checkbox" name="cc_myself" /></td></tr> - >>> print f.as_ul() - <li><ul class="errorlist"><li>This field is required.</li></ul>Subject: <input type="text" name="subject" maxlength="100" /></li> - <li>Message: <input type="text" name="message" value="Hi there" /></li> - <li><ul class="errorlist"><li>Enter a valid e-mail address.</li></ul>Sender: <input type="text" name="sender" value="invalid e-mail address" /></li> - <li>Cc myself: <input checked="checked" type="checkbox" name="cc_myself" /></li> - >>> print f.as_p() - <p><ul class="errorlist"><li>This field is required.</li></ul></p> - <p>Subject: <input type="text" name="subject" maxlength="100" /></p> - <p>Message: <input type="text" name="message" value="Hi there" /></p> - <p><ul class="errorlist"><li>Enter a valid e-mail address.</li></ul></p> - <p>Sender: <input type="text" name="sender" value="invalid e-mail address" /></p> - <p>Cc myself: <input checked="checked" type="checkbox" name="cc_myself" /></p> - -Customizing the error list format -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -By default, forms use ``django.newforms.util.ErrorList`` to format validation -errors. If you'd like to use an alternate class for displaying errors, you can -pass that in at construction time:: - - >>> from django.newforms.util import ErrorList - >>> class DivErrorList(ErrorList): - ... def __unicode__(self): - ... return self.as_divs() - ... def as_divs(self): - ... if not self: return u'' - ... return u'<div class="errorlist">%s</div>' % ''.join([u'<div class="error">%s</div>' % e for e in self]) - >>> f = ContactForm(data, auto_id=False, error_class=DivErrorList) - >>> f.as_p() - <div class="errorlist"><div class="error">This field is required.</div></div> - <p>Subject: <input type="text" name="subject" maxlength="100" /></p> - <p>Message: <input type="text" name="message" value="Hi there" /></p> - <div class="errorlist"><div class="error">Enter a valid e-mail address.</div></div> - <p>Sender: <input type="text" name="sender" value="invalid e-mail address" /></p> - <p>Cc myself: <input checked="checked" type="checkbox" name="cc_myself" /></p> - -More granular output -~~~~~~~~~~~~~~~~~~~~ - -The ``as_p()``, ``as_ul()`` and ``as_table()`` methods are simply shortcuts for -lazy developers -- they're not the only way a form object can be displayed. - -To display the HTML for a single field in your form, use dictionary lookup -syntax using the field's name as the key, and print the resulting object:: - - >>> f = ContactForm() - >>> print f['subject'] - <input id="id_subject" type="text" name="subject" maxlength="100" /> - >>> print f['message'] - <input type="text" name="message" id="id_message" /> - >>> print f['sender'] - <input type="text" name="sender" id="id_sender" /> - >>> print f['cc_myself'] - <input type="checkbox" name="cc_myself" id="id_cc_myself" /> - -Call ``str()`` or ``unicode()`` on the field to get its rendered HTML as a -string or Unicode object, respectively:: - - >>> str(f['subject']) - '<input id="id_subject" type="text" name="subject" maxlength="100" />' - >>> unicode(f['subject']) - u'<input id="id_subject" type="text" name="subject" maxlength="100" />' - -The field-specific output honors the form object's ``auto_id`` setting:: - - >>> f = ContactForm(auto_id=False) - >>> print f['message'] - <input type="text" name="message" /> - >>> f = ContactForm(auto_id='id_%s') - >>> print f['message'] - <input type="text" name="message" id="id_message" /> - -For a field's list of errors, access the field's ``errors`` attribute. This -is a list-like object that is displayed as an HTML ``<ul class="errorlist">`` -when printed:: - - >>> data = {'subject': 'hi', 'message': '', 'sender': '', 'cc_myself': ''} - >>> f = ContactForm(data, auto_id=False) - >>> print f['message'] - <input type="text" name="message" /> - >>> f['message'].errors - [u'This field is required.'] - >>> print f['message'].errors - <ul class="errorlist"><li>This field is required.</li></ul> - >>> f['subject'].errors - [] - >>> print f['subject'].errors - - >>> str(f['subject'].errors) - '' - -Using forms in views and templates ----------------------------------- - -Let's put this all together and use the ``ContactForm`` example in a Django -view and template. - -Simple view example -~~~~~~~~~~~~~~~~~~~ - -This example view displays the contact form by default and validates/processes -it if accessed via a POST request:: - - def contact(request): - if request.method == 'POST': - form = ContactForm(request.POST) - if form.is_valid(): - # Do form processing here... - return HttpResponseRedirect('/url/on_success/') - else: - form = ContactForm() - return render_to_response('contact.html', {'form': form}) - -Simple template example -~~~~~~~~~~~~~~~~~~~~~~~ - -The template in the above view example, ``contact.html``, is responsible for -displaying the form as HTML. To do this, we can use the techniques outlined in -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" action=""> - <table>{{ form }}</table> - <input type="submit" /> - </form> - -The above template code will display the form as an HTML table, using the -``form.as_table()`` method explained previously. This works because Django's -template system displays an object's ``__str__()`` value, and the ``Form`` -class' ``__str__()`` method calls its ``as_table()`` method. - -The following is equivalent but a bit more explicit:: - - <form method="post" action=""> - <table>{{ form.as_table }}</table> - <input type="submit" /> - </form> - -``form.as_ul`` and ``form.as_p`` are also available, as you may expect. - -Note that in the above two examples, we included the ``<form>``, ``<table>`` -``<input type="submit" />``, ``</table>`` and ``</form>`` tags. The form -convenience methods (``as_table()``, ``as_ul()`` and ``as_p()``) do not include -that HTML. - -Complex template output -~~~~~~~~~~~~~~~~~~~~~~~ - -As we've stressed several times, the ``as_table()``, ``as_ul()`` and ``as_p()`` -methods are just shortcuts for the common case. You can also work with the -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" action=""> - <dl> - {% for field in form %} - <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 %} - {% endfor %} - </dl> - <input type="submit" /> - </form> - -This iteration technique is useful if you want to apply the same HTML -formatting to each field, or if you don't know the names of the form fields -ahead of time. Note that the fields will be iterated over in the order in which -they're defined in the ``Form`` class. - -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" action=""> - <ul class="myformclass"> - <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_tag }} {{ form.subject }}</li> - <li class="helptext">{{ form.subject.help_text }}</li> - {% if form.subject.errors %}<ul class="errorlist">{{ form.subject.errors }}</ul>{% endif %} - - ... - </ul> - </form> - -Highlighting required fields in templates -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -It's common to show a user which fields are required. Here's an example of how -to do that, using the above example modified to insert an asterisk after the -label of each required field:: - - <form method="post" action=""> - <dl> - {% for field in form %} - <dt>{{ field.label_tag }}{% if field.field.required %}*{% endif %}</dt> - <dd>{{ field }}</dd> - {% if field.help_text %}<dd>{{ field.help_text }}</dd>{% endif %} - {% if field.errors %}<dd class="myerrors">{{ field.errors }}</dd>{% endif %} - {% endfor %} - </dl> - <input type="submit" /> - </form> - -The ``{% if field.field.required %}*{% endif %}`` fragment is the relevant -addition here. It adds the asterisk only if the field is required. - -Note that we check ``field.field.required`` and not ``field.required``. In the -template, ``field`` is a ``newforms.forms.BoundField`` instance, which holds -the actual ``Field`` instance in its ``field`` attribute. - -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 - >>> from django.core.files.uploadedfile import SimpleUploadedFile - >>> data = {'subject': 'hello', - ... 'message': 'Hi there', - ... 'sender': 'foo@example.com', - ... 'cc_myself': True} - >>> file_data = {'mugshot': SimpleUploadedFile('face.jpg', <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() - -Testing for multipart forms -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If you're writing reusable views or templates, you may not know ahead of time -whether your form is a multipart form or not. The ``is_multipart()`` method -tells you whether the form requires multipart encoding for submission:: - - >>> f = ContactFormWithMugshot() - >>> f.is_multipart() - True - -Here's an example of how you might use this in a template:: - - {% if form.is_multipart %} - <form enctype="multipart/form-data" method="post" action="/foo/"> - {% else %} - <form method="post" action="/foo/"> - {% endif %} - {% form %} - </form> - -Subclassing forms ------------------ - -If you have multiple ``Form`` classes that share fields, you can use -subclassing to remove redundancy. - -When you subclass a custom ``Form`` class, the resulting subclass will -include all fields of the parent class(es), followed by the fields you define -in the subclass. - -In this example, ``ContactFormWithPriority`` contains all the fields from -``ContactForm``, plus an additional field, ``priority``. The ``ContactForm`` -fields are ordered first:: - - >>> class ContactFormWithPriority(ContactForm): - ... priority = forms.CharField() - >>> f = ContactFormWithPriority(auto_id=False) - >>> print f.as_ul() - <li>Subject: <input type="text" name="subject" maxlength="100" /></li> - <li>Message: <input type="text" name="message" /></li> - <li>Sender: <input type="text" name="sender" /></li> - <li>Cc myself: <input type="checkbox" name="cc_myself" /></li> - <li>Priority: <input type="text" name="priority" /></li> - -It's possible to subclass multiple forms, treating forms as "mix-ins." In this -example, ``BeatleForm`` subclasses both ``PersonForm`` and ``InstrumentForm`` -(in that order), and its field list includes the fields from the parent -classes:: - - >>> class PersonForm(Form): - ... first_name = CharField() - ... last_name = CharField() - >>> class InstrumentForm(Form): - ... instrument = CharField() - >>> class BeatleForm(PersonForm, InstrumentForm): - ... haircut_type = CharField() - >>> b = BeatleForm(auto_id=False) - >>> print b.as_ul() - <li>First name: <input type="text" name="first_name" /></li> - <li>Last name: <input type="text" name="last_name" /></li> - <li>Instrument: <input type="text" name="instrument" /></li> - <li>Haircut type: <input type="text" name="haircut_type" /></li> - -Prefixes for forms ------------------- - -You can put several Django forms inside one ``<form>`` tag. To give each -``Form`` its own namespace, use the ``prefix`` keyword argument:: - - >>> mother = PersonForm(prefix="mother") - >>> father = PersonForm(prefix="father") - >>> print mother.as_ul() - <li><label for="id_mother-first_name">First name:</label> <input type="text" name="mother-first_name" id="id_mother-first_name" /></li> - <li><label for="id_mother-last_name">Last name:</label> <input type="text" name="mother-last_name" id="id_mother-last_name" /></li> - >>> print father.as_ul() - <li><label for="id_father-first_name">First name:</label> <input type="text" name="father-first_name" id="id_father-first_name" /></li> - <li><label for="id_father-last_name">Last name:</label> <input type="text" name="father-last_name" id="id_father-last_name" /></li> - -Fields -====== - -When you create a ``Form`` class, the most important part is defining the -fields of the form. Each field has custom validation logic, along with a few -other hooks. - -Although the primary way you'll use ``Field`` classes is in ``Form`` classes, -you can also instantiate them and use them directly to get a better idea of -how they work. Each ``Field`` instance has a ``clean()`` method, which takes -a single argument and either raises a ``django.newforms.ValidationError`` -exception or returns the clean value:: - - >>> f = forms.EmailField() - >>> f.clean('foo@example.com') - u'foo@example.com' - >>> f.clean(u'foo@example.com') - u'foo@example.com' - >>> f.clean('invalid e-mail address') - Traceback (most recent call last): - ... - ValidationError: [u'Enter a valid e-mail address.'] - -If you've used Django's old forms/validation framework, take care in noticing -this ``ValidationError`` is different than the previous ``ValidationError``. -This one lives at ``django.newforms.ValidationError`` rather than -``django.core.validators.ValidationError``. - -Core field arguments --------------------- - -Each ``Field`` class constructor takes at least these arguments. Some -``Field`` classes take additional, field-specific arguments, but the following -should *always* be accepted: - -``required`` -~~~~~~~~~~~~ - -By default, each ``Field`` class assumes the value is required, so if you pass -an empty value -- either ``None`` or the empty string (``""``) -- then -``clean()`` will raise a ``ValidationError`` exception:: - - >>> f = forms.CharField() - >>> f.clean('foo') - u'foo' - >>> f.clean('') - Traceback (most recent call last): - ... - ValidationError: [u'This field is required.'] - >>> f.clean(None) - Traceback (most recent call last): - ... - ValidationError: [u'This field is required.'] - >>> f.clean(' ') - u' ' - >>> f.clean(0) - u'0' - >>> f.clean(True) - u'True' - >>> f.clean(False) - u'False' - -To specify that a field is *not* required, pass ``required=False`` to the -``Field`` constructor:: - - >>> f = forms.CharField(required=False) - >>> f.clean('foo') - u'foo' - >>> f.clean('') - u'' - >>> f.clean(None) - u'' - >>> f.clean(0) - u'0' - >>> f.clean(True) - u'True' - >>> f.clean(False) - u'False' - -If a ``Field`` has ``required=False`` and you pass ``clean()`` an empty value, -then ``clean()`` will return a *normalized* empty value rather than raising -``ValidationError``. For ``CharField``, this will be a Unicode empty string. -For other ``Field`` classes, it might be ``None``. (This varies from field to -field.) - -``label`` -~~~~~~~~~ - -The ``label`` argument lets you specify the "human-friendly" label for this -field. This is used when the ``Field`` is displayed in a ``Form``. - -As explained in "Outputting forms as HTML" above, the default label for a -``Field`` is generated from the field name by converting all underscores to -spaces and upper-casing the first letter. Specify ``label`` if that default -behavior doesn't result in an adequate label. - -Here's a full example ``Form`` that implements ``label`` for two of its fields. -We've specified ``auto_id=False`` to simplify the output:: - - >>> class CommentForm(forms.Form): - ... name = forms.CharField(label='Your name') - ... url = forms.URLField(label='Your Web site', required=False) - ... comment = forms.CharField() - >>> f = CommentForm(auto_id=False) - >>> print f - <tr><th>Your name:</th><td><input type="text" name="name" /></td></tr> - <tr><th>Your Web site:</th><td><input type="text" name="url" /></td></tr> - <tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr> - -``initial`` -~~~~~~~~~~~ - -The ``initial`` argument lets you specify the initial value to use when -rendering this ``Field`` in an unbound ``Form``. - -The use-case for this is when you want to display an "empty" form in which a -field is initialized to a particular value. For example:: - - >>> class CommentForm(forms.Form): - ... name = forms.CharField(initial='Your name') - ... url = forms.URLField(initial='http://') - ... comment = forms.CharField() - >>> f = CommentForm(auto_id=False) - >>> print f - <tr><th>Name:</th><td><input type="text" name="name" value="Your name" /></td></tr> - <tr><th>Url:</th><td><input type="text" name="url" value="http://" /></td></tr> - <tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr> - -You may be thinking, why not just pass a dictionary of the initial values as -data when displaying the form? Well, if you do that, you'll trigger validation, -and the HTML output will include any validation errors:: - - >>> class CommentForm(forms.Form): - ... name = forms.CharField() - ... url = forms.URLField() - ... comment = forms.CharField() - >>> default_data = {'name': 'Your name', 'url': 'http://'} - >>> f = CommentForm(default_data, auto_id=False) - >>> print f - <tr><th>Name:</th><td><input type="text" name="name" value="Your name" /></td></tr> - <tr><th>Url:</th><td><ul class="errorlist"><li>Enter a valid URL.</li></ul><input type="text" name="url" value="http://" /></td></tr> - <tr><th>Comment:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="comment" /></td></tr> - -This is why ``initial`` values are only displayed for unbound forms. For bound -forms, the HTML output will use the bound data. - -Also note that ``initial`` values are *not* used as "fallback" data in -validation if a particular field's value is not given. ``initial`` values are -*only* intended for initial form display:: - - >>> class CommentForm(forms.Form): - ... name = forms.CharField(initial='Your name') - ... url = forms.URLField(initial='http://') - ... comment = forms.CharField() - >>> data = {'name': '', 'url': '', 'comment': 'Foo'} - >>> f = CommentForm(data) - >>> f.is_valid() - False - # The form does *not* fall back to using the initial values. - >>> f.errors - {'url': [u'This field is required.'], 'name': [u'This field is required.']} - -``widget`` -~~~~~~~~~~ - -The ``widget`` argument lets you specify a ``Widget`` class to use when -rendering this ``Field``. See `Widgets`_ below for more information. - -``help_text`` -~~~~~~~~~~~~~ - -The ``help_text`` argument lets you specify descriptive text for this -``Field``. If you provide ``help_text``, it will be displayed next to the -``Field`` when the ``Field`` is rendered by one of the convenience ``Form`` -methods (e.g., ``as_ul()``). - -Here's a full example ``Form`` that implements ``help_text`` for two of its -fields. We've specified ``auto_id=False`` to simplify the output:: - - >>> class HelpTextContactForm(forms.Form): - ... subject = forms.CharField(max_length=100, help_text='100 characters max.') - ... message = forms.CharField() - ... sender = forms.EmailField(help_text='A valid e-mail address, please.') - ... cc_myself = forms.BooleanField(required=False) - >>> f = HelpTextContactForm(auto_id=False) - >>> print f.as_table() - <tr><th>Subject:</th><td><input type="text" name="subject" maxlength="100" /><br />100 characters max.</td></tr> - <tr><th>Message:</th><td><input type="text" name="message" /></td></tr> - <tr><th>Sender:</th><td><input type="text" name="sender" /><br />A valid e-mail address, please.</td></tr> - <tr><th>Cc myself:</th><td><input type="checkbox" name="cc_myself" /></td></tr> - >>> print f.as_ul() - <li>Subject: <input type="text" name="subject" maxlength="100" /> 100 characters max.</li> - <li>Message: <input type="text" name="message" /></li> - <li>Sender: <input type="text" name="sender" /> A valid e-mail address, please.</li> - <li>Cc myself: <input type="checkbox" name="cc_myself" /></li> - >>> print f.as_p() - <p>Subject: <input type="text" name="subject" maxlength="100" /> 100 characters max.</p> - <p>Message: <input type="text" name="message" /></p> - <p>Sender: <input type="text" name="sender" /> A valid e-mail address, please.</p> - <p>Cc myself: <input type="checkbox" name="cc_myself" /></p> - -``error_messages`` -~~~~~~~~~~~~~~~~~~ - -**New in Django development version** - -The ``error_messages`` argument lets you override the default messages that the -field will raise. Pass in a dictionary with keys matching the error messages you -want to override. For example, here is the default error message:: - - >>> generic = forms.CharField() - >>> generic.clean('') - Traceback (most recent call last): - ... - ValidationError: [u'This field is required.'] - -And here is a custom error message:: - - >>> name = forms.CharField(error_messages={'required': 'Please enter your name'}) - >>> name.clean('') - Traceback (most recent call last): - ... - ValidationError: [u'Please enter your name'] - -In the `built-in Field classes`_ section below, each ``Field`` defines the -error message keys it uses. - -Dynamic initial values ----------------------- - -The ``initial`` argument to ``Field`` (explained above) lets you hard-code the -initial value for a ``Field`` -- but what if you want to declare the initial -value at runtime? For example, you might want to fill in a ``username`` field -with the username of the current session. - -To accomplish this, use the ``initial`` argument to a ``Form``. This argument, -if given, should be a dictionary mapping field names to initial values. Only -include the fields for which you're specifying an initial value; it's not -necessary to include every field in your form. For example:: - - >>> class CommentForm(forms.Form): - ... name = forms.CharField() - ... url = forms.URLField() - ... comment = forms.CharField() - >>> f = CommentForm(initial={'name': 'your username'}, auto_id=False) - >>> print f - <tr><th>Name:</th><td><input type="text" name="name" value="your username" /></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> - >>> f = CommentForm(initial={'name': 'another username'}, auto_id=False) - >>> print f - <tr><th>Name:</th><td><input type="text" name="name" value="another username" /></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> - -Just like the ``initial`` parameter to ``Field``, these values are only -displayed for unbound forms, and they're not used as fallback values if a -particular value isn't provided. - -Finally, note that if a ``Field`` defines ``initial`` *and* you include -``initial`` when instantiating the ``Form``, then the latter ``initial`` will -have precedence. In this example, ``initial`` is provided both at the field -level and at the form instance level, and the latter gets precedence:: - - >>> class CommentForm(forms.Form): - ... name = forms.CharField(initial='class') - ... url = forms.URLField() - ... comment = forms.CharField() - >>> f = CommentForm(initial={'name': 'instance'}, auto_id=False) - >>> print f - <tr><th>Name:</th><td><input type="text" name="name" value="instance" /></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> - -Built-in ``Field`` classes --------------------------- - -Naturally, the ``newforms`` library comes with a set of ``Field`` classes that -represent common validation needs. This section documents each built-in field. - -For each field, we describe the default widget used if you don't specify -``widget``. We also specify the value returned when you provide an empty value -(see the section on ``required`` above to understand what that means). - -``BooleanField`` -~~~~~~~~~~~~~~~~ - - * Default widget: ``CheckboxInput`` - * Empty value: ``False`` - * Normalizes to: A Python ``True`` or ``False`` value. - * Validates that the check box is checked (i.e. the value is ``True``) if - the field has ``required=True``. - * Error message keys: ``required`` - -**New in Django development version:** The empty value for a ``CheckboxInput`` -(and hence the standard ``BooleanField``) has changed to return ``False`` -instead of ``None`` in the development version. - -.. note:: - Since all ``Field`` subclasses have ``required=True`` by default, the - validation condition here is important. If you want to include a checkbox - in your form that can be either checked or unchecked, you must remember to - pass in ``required=False`` when creating the ``BooleanField``. - -``CharField`` -~~~~~~~~~~~~~ - - * Default widget: ``TextInput`` - * Empty value: ``''`` (an empty string) - * Normalizes to: A Unicode object. - * Validates ``max_length`` or ``min_length``, if they are provided. - Otherwise, all inputs are valid. - * Error message keys: ``required``, ``max_length``, ``min_length`` - -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. - -``ChoiceField`` -~~~~~~~~~~~~~~~ - - * Default widget: ``Select`` - * Empty value: ``''`` (an empty string) - * Normalizes to: A Unicode object. - * Validates that the given value exists in the list of choices. - * Error message keys: ``required``, ``invalid_choice`` - -Takes one extra argument, ``choices``, which is an iterable (e.g., a list or -tuple) of 2-tuples to use as choices for this field. This argument accepts -the same formats as the ``choices`` argument to a model field. See the -`model API documentation on choices`_ for more details. - -.. _model API documentation on choices: ../model-api#choices - -``DateField`` -~~~~~~~~~~~~~ - - * Default widget: ``TextInput`` - * Empty value: ``None`` - * Normalizes to: A Python ``datetime.date`` object. - * Validates that the given value is either a ``datetime.date``, - ``datetime.datetime`` or string formatted in a particular date format. - * Error message keys: ``required``, ``invalid`` - -Takes one optional argument, ``input_formats``, which is a list of formats used -to attempt to convert a string to a valid ``datetime.date`` object. - -If no ``input_formats`` argument is provided, the default input formats are:: - - '%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', # '2006-10-25', '10/25/2006', '10/25/06' - '%b %d %Y', '%b %d, %Y', # 'Oct 25 2006', 'Oct 25, 2006' - '%d %b %Y', '%d %b, %Y', # '25 Oct 2006', '25 Oct, 2006' - '%B %d %Y', '%B %d, %Y', # 'October 25 2006', 'October 25, 2006' - '%d %B %Y', '%d %B, %Y', # '25 October 2006', '25 October, 2006' - -``DateTimeField`` -~~~~~~~~~~~~~~~~~ - - * Default widget: ``DateTimeInput`` - * Empty value: ``None`` - * Normalizes to: A Python ``datetime.datetime`` object. - * Validates that the given value is either a ``datetime.datetime``, - ``datetime.date`` or string formatted in a particular datetime format. - * Error message keys: ``required``, ``invalid`` - -Takes one optional argument, ``input_formats``, which is a list of formats used -to attempt to convert a string to a valid ``datetime.datetime`` object. - -If no ``input_formats`` argument is provided, the default input formats are:: - - '%Y-%m-%d %H:%M:%S', # '2006-10-25 14:30:59' - '%Y-%m-%d %H:%M', # '2006-10-25 14:30' - '%Y-%m-%d', # '2006-10-25' - '%m/%d/%Y %H:%M:%S', # '10/25/2006 14:30:59' - '%m/%d/%Y %H:%M', # '10/25/2006 14:30' - '%m/%d/%Y', # '10/25/2006' - '%m/%d/%y %H:%M:%S', # '10/25/06 14:30:59' - '%m/%d/%y %H:%M', # '10/25/06 14:30' - '%m/%d/%y', # '10/25/06' - -**New in Django development version:** The ``DateTimeField`` used to use a -``TextInput`` widget by default. This has now changed. - -``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. - * Error message keys: ``required``, ``invalid``, ``max_value``, - ``min_value``, ``max_digits``, ``max_decimal_places``, - ``max_whole_digits`` - -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`` -~~~~~~~~~~~~~~ - - * Default widget: ``TextInput`` - * Empty value: ``''`` (an empty string) - * Normalizes to: A Unicode object. - * Validates that the given value is a valid e-mail address, using a - moderately complex regular expression. - * Error message keys: ``required``, ``invalid`` - -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. - * Error message keys: ``required``, ``invalid``, ``missing``, ``empty`` - -To learn more about the ``UploadedFile`` object, see the `file uploads documentation`_. - -When you use a ``FileField`` in a form, you must also remember to -`bind the file data to the form`_. - -.. _file uploads documentation: ../upload_handling/ -.. _`bind the file data to the form`: `Binding uploaded files to a form`_ - -``FilePathField`` -~~~~~~~~~~~~~~~~~ - -**New in Django development version** - - * Default widget: ``Select`` - * Empty value: ``None`` - * Normalizes to: A unicode object - * Validates that the selected choice exists in the list of choices. - * Error message keys: ``required``, ``invalid_choice`` - -The field allows choosing from files inside a certain directory. It takes three -extra arguments: - - ============== ========== =============================================== - Argument Required? Description - ============== ========== =============================================== - ``path`` Yes The absolute path to the directory whose - contents you want listed. This directory must - exist. - - ``recursive`` No If ``False`` (the default) only the direct - contents of ``path`` will be offered as choices. - If ``True``, the directory will be descended - into recursively and all descendants will be - listed as choices. - - ``match`` No A regular expression pattern; only files with - names matching this expression will be allowed - as choices. - ============== ========== =============================================== - -``FloatField`` -~~~~~~~~~~~~~~ - - * Default widget: ``TextInput`` - * Empty value: ``None`` - * Normalizes to: A Python float. - * Validates that the given value is an float. Leading and trailing - whitespace is allowed, as in Python's ``float()`` function. - * Error message keys: ``required``, ``invalid``, ``max_value``, - ``min_value`` - -Takes two optional arguments for validation, ``max_value`` and ``min_value``. -These control the range of values permitted in the field. - -``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. - * Error message keys: ``required``, ``invalid``, ``missing``, ``empty``, - ``invalid_image`` - -Using an ImageField requires that the `Python Imaging Library`_ is installed. - -When you use an ``ImageField`` in a form, you must also remember to -`bind the file data to the form`_. - -.. _Python Imaging Library: http://www.pythonware.com/products/pil/ - -``IntegerField`` -~~~~~~~~~~~~~~~~ - - * Default widget: ``TextInput`` - * Empty value: ``None`` - * Normalizes to: A Python integer or long integer. - * Validates that the given value is an integer. Leading and trailing - whitespace is allowed, as in Python's ``int()`` function. - * Error message keys: ``required``, ``invalid``, ``max_value``, - ``min_value`` - -Takes two optional arguments for validation, ``max_value`` and ``min_value``. -These control the range of values permitted in the field. - -``IPAddressField`` -~~~~~~~~~~~~~~~~~~ - - * Default widget: ``TextInput`` - * Empty value: ``''`` (an empty string) - * Normalizes to: A Unicode object. - * Validates that the given value is a valid IPv4 address, using a regular - expression. - * Error message keys: ``required``, ``invalid`` - -``MultipleChoiceField`` -~~~~~~~~~~~~~~~~~~~~~~~ - - * Default widget: ``SelectMultiple`` - * Empty value: ``[]`` (an empty list) - * Normalizes to: A list of Unicode objects. - * Validates that every value in the given list of values exists in the list - of choices. - * Error message keys: ``required``, ``invalid_choice``, ``invalid_list`` - -Takes one extra argument, ``choices``, which is an iterable (e.g., a list or -tuple) of 2-tuples to use as choices for this field. This argument accepts -the same formats as the ``choices`` argument to a model field. See the -`model API documentation on choices`_ for more details. - -``NullBooleanField`` -~~~~~~~~~~~~~~~~~~~~ - - * Default widget: ``NullBooleanSelect`` - * Empty value: ``None`` - * Normalizes to: A Python ``True``, ``False`` or ``None`` value. - * Validates nothing (i.e., it never raises a ``ValidationError``). - -``RegexField`` -~~~~~~~~~~~~~~ - - * Default widget: ``TextInput`` - * Empty value: ``''`` (an empty string) - * Normalizes to: A Unicode object. - * Validates that the given value matches against a certain regular - expression. - * Error message keys: ``required``, ``invalid`` - -Takes one required argument, ``regex``, which is a regular expression specified -either as a string or a compiled regular expression object. - -Also takes the following optional arguments: - - ====================== ===================================================== - Argument Description - ====================== ===================================================== - ``max_length`` Ensures the string has at most this many characters. - ``min_length`` Ensures the string has at least this many characters. - ====================== ===================================================== - -The optional argument ``error_message`` is also accepted for backwards -compatibility. The preferred way to provide an error message is to use the -``error_messages`` argument, passing a dictionary with ``'invalid'`` as a key -and the error message as the value. - -``TimeField`` -~~~~~~~~~~~~~ - - * Default widget: ``TextInput`` - * Empty value: ``None`` - * Normalizes to: A Python ``datetime.time`` object. - * Validates that the given value is either a ``datetime.time`` or string - formatted in a particular time format. - * Error message keys: ``required``, ``invalid`` - -Takes one optional argument, ``input_formats``, which is a list of formats used -to attempt to convert a string to a valid ``datetime.time`` object. - -If no ``input_formats`` argument is provided, the default input formats are:: - - '%H:%M:%S', # '14:30:59' - '%H:%M', # '14:30' - -``URLField`` -~~~~~~~~~~~~ - - * Default widget: ``TextInput`` - * Empty value: ``''`` (an empty string) - * Normalizes to: A Unicode object. - * Validates that the given value is a valid URL. - * Error message keys: ``required``, ``invalid``, ``invalid_link`` - -Takes the following optional arguments: - - ======================== ===================================================== - Argument Description - ======================== ===================================================== - ``max_length`` Ensures the string has at most this many characters. - ``min_length`` Ensures the string has at least this many characters. - ``verify_exists`` If ``True``, the validator will attempt to load the - given URL, raising ``ValidationError`` if the page - gives a 404. Defaults to ``False``. - ``validator_user_agent`` String used as the user-agent used when checking for - a URL's existence. Defaults to the value of the - ``URL_VALIDATOR_USER_AGENT`` setting. - ======================== ===================================================== - -Slightly complex built-in ``Field`` classes -------------------------------------------- - -The following are not yet documented here. See the unit tests, linked-to from -the bottom of this document, for examples of their use. - -``ComboField`` -~~~~~~~~~~~~~~ - -``MultiValueField`` -~~~~~~~~~~~~~~~~~~~ - -``SplitDateTimeField`` -~~~~~~~~~~~~~~~~~~~~~~ - -Fields which handle relationships ---------------------------------- - -For representing relationships between models, two fields are -provided which can derive their choices from a ``QuerySet``, and which -place one or more model objects into the ``cleaned_data`` dictionary -of forms in which they're used. Both of these fields have an -additional required argument: - -``queryset`` - A ``QuerySet`` of model objects from which the choices for the - field will be derived, and which will be used to validate the - user's selection. - -``ModelChoiceField`` -~~~~~~~~~~~~~~~~~~~~ - -Allows the selection of a single model object, suitable for -representing a foreign key. - -The ``__unicode__`` method of the model will be called to generate -string representations of the objects for use in the field's choices; -to provide customized representations, subclass ``ModelChoiceField`` -and override ``label_from_instance``. This method will receive a model -object, and should return a string suitable for representing it. For -example:: - - class MyModelChoiceField(ModelChoiceField): - def label_from_instance(self, obj): - return "My Object #%i" % obj.id - -``ModelMultipleChoiceField`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Allows the selection of one or more model objects, suitable for -representing a many-to-many relation. As with ``ModelChoiceField``, -you can use ``label_from_instance`` to customize the object -representations. - -Creating custom fields ----------------------- - -If the built-in ``Field`` classes don't meet your needs, you can easily create -custom ``Field`` classes. To do this, just create a subclass of -``django.newforms.Field``. Its only requirements are that it implement a -``clean()`` method and that its ``__init__()`` method accept the core arguments -mentioned above (``required``, ``label``, ``initial``, ``widget``, -``help_text``). - -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. 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()`` -directly), but normally they won't be needed. - -In general, any cleaning method can raise ``ValidationError`` if there is a -problem with the data it is processing, passing the relevant error message to -the ``ValidationError`` constructor. If no ``ValidationError`` is raised, the -method should return the cleaned (normalised) data as a Python object. - -If you detect multiple errors during a cleaning method and wish to signal all -of them to the form submitter, it is possible to pass a list of errors to the -``ValidationError`` constructor. - -The three types of cleaning methods are: - - * The ``clean()`` method on a Field subclass. This is responsible - for cleaning the data in a way that is generic for that type of field. - For example, a FloatField will turn the data into a Python ``float`` or - raise a ``ValidationError``. - - * The ``clean_<fieldname>()`` method in a form subclass -- where - ``<fieldname>`` is replaced with the name of the form field attribute. - This method does any cleaning that is specific to that particular - attribute, unrelated to the type of field that it is. This method is not - passed any parameters. You will need to look up the value of the field - in ``self.cleaned_data`` and remember that it will be a Python object - at this point, not the original string submitted in the form (it will be - in ``cleaned_data`` because the general field ``clean()`` method, above, - has already cleaned the data once). - - For example, if you wanted to validate that the contents of a - ``CharField`` called ``serialnumber`` was unique, - ``clean_serialnumber()`` would be the right place to do this. You don't - need a specific field (it's just a ``CharField``), but you want a - formfield-specific piece of validation and, possibly, - cleaning/normalizing the data. - - * The Form subclass's ``clean()`` method. This method can perform - any validation that requires access to multiple fields from the form at - once. This is where you might put in things to check that if field ``A`` - is supplied, field ``B`` must contain a valid email address and the - like. The data that this method returns is the final ``cleaned_data`` - attribute for the form, so don't forget to return the full list of - cleaned data if you override this method (by default, ``Form.clean()`` - just returns ``self.cleaned_data``). - - Note that any errors raised by your ``Form.clean()`` override will not - be associated with any field in particular. They go into a special - "field" (called ``__all__``), which you can access via the - ``non_field_errors()`` method if you need to. - -These methods are run in the order given above, one field at a time. That is, -for each field in the form (in the order they are declared in the form -definition), the ``Field.clean()`` method (or its override) is run, then -``clean_<fieldname>()``. Finally, once those two methods are run for every -field, the ``Form.clean()`` method, or its override, is executed. - -As mentioned above, any of these methods can raise a ``ValidationError``. For -any field, if the ``Field.clean()`` method raises a ``ValidationError``, any -field-specific cleaning method is not called. However, the cleaning methods -for all remaining fields are still executed. - -The ``clean()`` method for the ``Form`` class or subclass is always run. If -that method raises a ``ValidationError``, ``cleaned_data`` will be an empty -dictionary. - -The previous paragraph means that if you are overriding ``Form.clean()``, you -should iterate through ``self.cleaned_data.items()``, possibly considering the -``_errors`` dictionary attribute on the form as well. In this way, you will -already know which fields have passed their individual validation requirements. - -A simple example -~~~~~~~~~~~~~~~~ - -Here's a simple example of a custom field that validates its input is a string -containing comma-separated e-mail addresses, with at least one address. We'll -keep it simple and assume e-mail validation is contained in a function called -``is_valid_email()``. The full class:: - - from django import newforms as forms - - 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) - return emails - -Let's alter the ongoing ``ContactForm`` example to demonstrate how you'd use -this in a form. Simply use ``MultiEmailField`` instead of ``forms.EmailField``, -like so:: - - class ContactForm(forms.Form): - subject = forms.CharField(max_length=100) - message = forms.CharField() - senders = MultiEmailField() - cc_myself = forms.BooleanField(required=False) - -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' ...`` - ``DateTimeInput`` ``<input type='text' ...`` - ``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. - ============================ =========================================== - -**New in Django development version:** The ``DateTimeInput`` has been added -since the last release. - -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 input 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 -default ``size`` attribute as a custom extension to the ``TextInput`` widget:: - - class CommentWidget(forms.TextInput): - def __init__(self, *args, **kwargs): - attrs = kwargs.setdefault('attrs',{}) - if 'size' not in attrs: - attrs['size'] = 40 - super(CommentWidget, self).__init__(*args, **kwargs) - -We allow the ``size`` attribute to be overridden by the user, but, by default, -this widget will behave as if ``attrs={'size': 40}`` was always passed into the -constructor. - -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 -=========================== - -The prefered way of generating forms that work with models is explained in the -`ModelForms documentation`_. - -Looking for the ``form_for_model`` and ``form_for_instance`` documentation? -They've been deprecated, but you can still `view the documentation`_. - -.. _ModelForms documentation: ../modelforms/ -.. _view the documentation: ../form_for_model/ - -Media -===== - -Rendering an attractive and easy-to-use web form requires more than just -HTML - it also requires CSS stylesheets, and if you want to use fancy -"Web2.0" widgets, you may also need to include some JavaScript on each -page. The exact combination of CSS and JavaScript that is required for -any given page will depend upon the widgets that are in use on that page. - -This is where Django media definitions come in. Django allows you to -associate different media files with the forms and widgets that require -that media. For example, if you want to use a calendar to render DateFields, -you can define a custom Calendar widget. This widget can then be associated -with the CSS and Javascript that is required to render the calendar. When -the Calendar widget is used on a form, Django is able to identify the CSS and -JavaScript files that are required, and provide the list of file names -in a form suitable for easy inclusion on your web page. - -.. admonition:: Media and Django Admin - - The Django Admin application defines a number of customized widgets - for calendars, filtered selections, and so on. These widgets define - media requirements, and the Django Admin uses the custom widgets - in place of the Django defaults. The Admin templates will only include - those media files that are required to render the widgets on any - given page. - - If you like the widgets that the Django Admin application uses, - feel free to use them in your own application! They're all stored - in ``django.contrib.admin.widgets``. - -.. admonition:: Which JavaScript toolkit? - - Many JavaScript toolkits exist, and many of them include widgets (such - as calendar widgets) that can be used to enhance your application. - Django has deliberately avoided blessing any one JavaScript toolkit. - Each toolkit has its own relative strengths and weaknesses - use - whichever toolkit suits your requirements. Django is able to integrate - with any JavaScript toolkit. - -Media as a static definition ----------------------------- - -The easiest way to define media is as a static definition. Using this method, -the media declaration is an inner class. The properties of the inner class -define the media requirements. - -Here's a simple example:: - - class CalendarWidget(forms.TextInput): - class Media: - css = { - 'all': ('pretty.css',) - } - js = ('animations.js', 'actions.js') - -This code defines a ``CalendarWidget``, which will be based on ``TextInput``. -Every time the CalendarWidget is used on a form, that form will be directed -to include the CSS file ``pretty.css``, and the JavaScript files -``animations.js`` and ``actions.js``. - -This static media definition is converted at runtime into a widget property -named ``media``. The media for a CalendarWidget instance can be retrieved -through this property:: - - >>> w = CalendarWidget() - >>> print w.media - <link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" /> - <script type="text/javascript" src="http://media.example.com/animations.js"></script> - <script type="text/javascript" src="http://media.example.com/actions.js"></script> - -Here's a list of all possible ``Media`` options. There are no required options. - -``css`` -~~~~~~~ - -A dictionary describing the CSS files required for various forms of output -media. - -The values in the dictionary should be a tuple/list of file names. See -`the section on media paths`_ for details of how to specify paths to media -files. - -.. _the section on media paths: `Paths in media definitions`_ - -The keys in the dictionary are the output media types. These are the same -types accepted by CSS files in media declarations: 'all', 'aural', 'braille', -'embossed', 'handheld', 'print', 'projection', 'screen', 'tty' and 'tv'. If -you need to have different stylesheets for different media types, provide -a list of CSS files for each output medium. The following example would -provide two CSS options -- one for the screen, and one for print:: - - class Media: - css = { - 'screen': ('pretty.css',), - 'print': ('newspaper.css',) - } - -If a group of CSS files are appropriate for multiple output media types, -the dictionary key can be a comma separated list of output media types. -In the following example, TV's and projectors will have the same media -requirements:: - - class Media: - css = { - 'screen': ('pretty.css',), - 'tv,projector': ('lo_res.css',), - 'print': ('newspaper.css',) - } - -If this last CSS definition were to be rendered, it would become the following HTML:: - - <link href="http://media.example.com/pretty.css" type="text/css" media="screen" rel="stylesheet" /> - <link href="http://media.example.com/lo_res.css" type="text/css" media="tv,projector" rel="stylesheet" /> - <link href="http://media.example.com/newspaper.css" type="text/css" media="print" rel="stylesheet" /> - -``js`` -~~~~~~ - -A tuple describing the required javascript files. See -`the section on media paths`_ for details of how to specify paths to media -files. - -``extend`` -~~~~~~~~~~ - -A boolean defining inheritance behavior for media declarations. - -By default, any object using a static media definition will inherit all the -media associated with the parent widget. This occurs regardless of how the -parent defines its media requirements. For example, if we were to extend our -basic Calendar widget from the example above:: - - class FancyCalendarWidget(CalendarWidget): - class Media: - css = { - 'all': ('fancy.css',) - } - js = ('whizbang.js',) - - >>> w = FancyCalendarWidget() - >>> print w.media - <link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" /> - <link href="http://media.example.com/fancy.css" type="text/css" media="all" rel="stylesheet" /> - <script type="text/javascript" src="http://media.example.com/animations.js"></script> - <script type="text/javascript" src="http://media.example.com/actions.js"></script> - <script type="text/javascript" src="http://media.example.com/whizbang.js"></script> - -The FancyCalendar widget inherits all the media from it's parent widget. If -you don't want media to be inherited in this way, add an ``extend=False`` -declaration to the media declaration:: - - class FancyCalendar(Calendar): - class Media: - extend = False - css = { - 'all': ('fancy.css',) - } - js = ('whizbang.js',) - - >>> w = FancyCalendarWidget() - >>> print w.media - <link href="http://media.example.com/fancy.css" type="text/css" media="all" rel="stylesheet" /> - <script type="text/javascript" src="http://media.example.com/whizbang.js"></script> - -If you require even more control over media inheritance, define your media -using a `dynamic property`_. Dynamic properties give you complete control over -which media files are inherited, and which are not. - -.. _dynamic property: `Media as a dynamic property`_ - -Media as a dynamic property ---------------------------- - -If you need to perform some more sophisticated manipulation of media -requirements, you can define the media property directly. This is done -by defining a model property that returns an instance of ``forms.Media``. -The constructor for ``forms.Media`` accepts ``css`` and ``js`` keyword -arguments in the same format as that used in a static media definition. - -For example, the static media definition for our Calendar Widget could -also be defined in a dynamic fashion:: - - class CalendarWidget(forms.TextInput): - def _media(self): - return forms.Media(css={'all': ('pretty.css',)}, - js=('animations.js', 'actions.js')) - media = property(_media) - -See the section on `Media objects`_ for more details on how to construct -return values for dynamic media properties. - -Paths in media definitions --------------------------- - -Paths used to specify media can be either relative or absolute. If a path -starts with '/', 'http://' or 'https://', it will be interpreted as an absolute -path, and left as-is. All other paths will be prepended with the value of -``settings.MEDIA_URL``. For example, if the MEDIA_URL for your site was -``http://media.example.com/``:: - - class CalendarWidget(forms.TextInput): - class Media: - css = { - 'all': ('/css/pretty.css',), - } - js = ('animations.js', 'http://othersite.com/actions.js') - - >>> w = CalendarWidget() - >>> print w.media - <link href="/css/pretty.css" type="text/css" media="all" rel="stylesheet" /> - <script type="text/javascript" src="http://media.example.com/animations.js"></script> - <script type="text/javascript" src="http://othersite.com/actions.js"></script> - -Media objects -------------- - -When you interrogate the media attribute of a widget or form, the value that -is returned is a ``forms.Media`` object. As we have already seen, the string -representation of a Media object is the HTML required to include media -in the ``<head>`` block of your HTML page. - -However, Media objects have some other interesting properties. - -Media subsets -~~~~~~~~~~~~~ - -If you only want media of a particular type, you can use the subscript operator -to filter out a medium of interest. For example:: - - >>> w = CalendarWidget() - >>> print w.media - <link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" /> - <script type="text/javascript" src="http://media.example.com/animations.js"></script> - <script type="text/javascript" src="http://media.example.com/actions.js"></script> - - >>> print w.media['css'] - <link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" /> - -When you use the subscript operator, the value that is returned is a new -Media object -- but one that only contains the media of interest. - -Combining media objects -~~~~~~~~~~~~~~~~~~~~~~~ - -Media objects can also be added together. When two media objects are added, -the resulting Media object contains the union of the media from both files:: - - class CalendarWidget(forms.TextInput): - class Media: - css = { - 'all': ('pretty.css',) - } - js = ('animations.js', 'actions.js') - - class OtherWidget(forms.TextInput): - class Media: - js = ('whizbang.js',) - - >>> w1 = CalendarWidget() - >>> w2 = OtherWidget() - >>> print w1+w2 - <link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" /> - <script type="text/javascript" src="http://media.example.com/animations.js"></script> - <script type="text/javascript" src="http://media.example.com/actions.js"></script> - <script type="text/javascript" src="http://media.example.com/whizbang.js"></script> - -Media on Forms --------------- - -Widgets aren't the only objects that can have media definitions -- forms -can also define media. The rules for media definitions on forms are the -same as the rules for widgets: declarations can be static or dynamic; -path and inheritance rules for those declarations are exactly the same. - -Regardless of whether you define a media declaration, *all* Form objects -have a media property. The default value for this property is the result -of adding the media definitions for all widgets that are part of the form:: - - class ContactForm(forms.Form): - date = DateField(widget=CalendarWidget) - name = CharField(max_length=40, widget=OtherWidget) - - >>> f = ContactForm() - >>> f.media - <link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" /> - <script type="text/javascript" src="http://media.example.com/animations.js"></script> - <script type="text/javascript" src="http://media.example.com/actions.js"></script> - <script type="text/javascript" src="http://media.example.com/whizbang.js"></script> - -If you want to associate additional media with a form -- for example, CSS for form -layout -- simply add a media declaration to the form:: - - class ContactForm(forms.Form): - date = DateField(widget=CalendarWidget) - name = CharField(max_length=40, widget=OtherWidget) - - class Media: - css = { - 'all': ('layout.css',) - } - - >>> f = ContactForm() - >>> f.media - <link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" /> - <link href="http://media.example.com/layout.css" type="text/css" media="all" rel="stylesheet" /> - <script type="text/javascript" src="http://media.example.com/animations.js"></script> - <script type="text/javascript" src="http://media.example.com/actions.js"></script> - <script type="text/javascript" src="http://media.example.com/whizbang.js"></script> - -Formsets -======== - -A formset is a layer of abstraction to working with multiple forms on the same -page. It can be best compared to a data grid. Let's say you have the following -form:: - - >>> from django import newforms as forms - >>> class ArticleForm(forms.Form): - ... title = forms.CharField() - ... pub_date = forms.DateField() - -You might want to allow the user to create several articles at once. To create -a formset of out of an ``ArticleForm`` you would do:: - - >>> from django.newforms.formsets import formset_factory - >>> ArticleFormSet = formset_factory(ArticleForm) - -You now have created a formset named ``ArticleFormSet``. The formset gives you -the ability to iterate over the forms in the formset and display them as you -would with a regular form:: - - >>> formset = ArticleFormSet() - >>> for form in formset.forms: - ... print form.as_table() - <tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" id="id_form-0-title" /></td></tr> - <tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" id="id_form-0-pub_date" /></td></tr> - -As you can see it only displayed one form. This is because by default the -``formset_factory`` defines one extra form. This can be controlled with the -``extra`` parameter:: - - >>> ArticleFormSet = formset_factory(ArticleForm, extra=2) - -Using initial data with a formset ---------------------------------- - -Initial data is what drives the main usability of a formset. As shown above -you can define the number of extra forms. What this means is that you are -telling the formset how many additional forms to show in addition to the -number of forms it generates from the initial data. Lets take a look at an -example:: - - >>> ArticleFormSet = formset_factory(ArticleForm, extra=2) - >>> formset = ArticleFormSet(initial=[ - ... {'title': u'Django is now open source', - ... 'pub_date': datetime.date.today()}, - ... ]) - - >>> for form in formset.forms: - ... print form.as_table() - <tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" value="Django is now open source" id="id_form-0-title" /></td></tr> - <tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" value="2008-05-12" id="id_form-0-pub_date" /></td></tr> - <tr><th><label for="id_form-1-title">Title:</label></th><td><input type="text" name="form-1-title" id="id_form-1-title" /></td></tr> - <tr><th><label for="id_form-1-pub_date">Pub date:</label></th><td><input type="text" name="form-1-pub_date" id="id_form-1-pub_date" /></td></tr> - <tr><th><label for="id_form-2-title">Title:</label></th><td><input type="text" name="form-2-title" id="id_form-2-title" /></td></tr> - <tr><th><label for="id_form-2-pub_date">Pub date:</label></th><td><input type="text" name="form-2-pub_date" id="id_form-2-pub_date" /></td></tr> - -There are now a total of three forms showing above. One for the initial data -that was passed in and two extra forms. Also note that we are passing in a -list of dictionaries as the initial data. - -Limiting the maximum number of forms ------------------------------------- - -The ``max_num`` parameter to ``formset_factory`` gives you the ability to -force the maximum number of forms the formset will display:: - - >>> ArticleFormSet = formset_factory(ArticleForm, extra=2, max_num=1) - >>> formset = ArticleFormset() - >>> for form in formset.forms: - ... print form.as_table() - <tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" id="id_form-0-title" /></td></tr> - <tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" id="id_form-0-pub_date" /></td></tr> - -The default value of ``max_num`` is ``0`` which is the same as saying put no -limit on the number forms displayed. - -Formset validation ------------------- - -Validation with a formset is about identical to a regular ``Form``. There is -an ``is_valid`` method on the formset to provide a convenient way to validate -each form in the formset:: - - >>> ArticleFormSet = formset_factory(ArticleForm) - >>> formset = ArticleFormSet({}) - >>> formset.is_valid() - True - -We passed in no data to the formset which is resulting in a valid form. The -formset is smart enough to ignore extra forms that were not changed. If we -attempt to provide an article, but fail to do so:: - - >>> data = { - ... 'form-TOTAL_FORMS': u'1', - ... 'form-INITIAL_FORMS': u'1', - ... 'form-0-title': u'Test', - ... 'form-0-pub_date': u'', - ... } - >>> formset = ArticleFormSet(data) - >>> formset.is_valid() - False - >>> formset.errors - [{'pub_date': [u'This field is required.']}] - -As we can see the formset properly performed validation and gave us the -expected errors. - -Understanding the ManagementForm -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You may have noticed the additional data that was required in the formset's -data above. This data is coming from the ``ManagementForm``. This form is -dealt with internally to the formset. If you don't use it, it will result in -an exception:: - - >>> data = { - ... 'form-0-title': u'Test', - ... 'form-0-pub_date': u'', - ... } - >>> formset = ArticleFormSet(data) - Traceback (most recent call last): - ... - django.newforms.util.ValidationError: [u'ManagementForm data is missing or has been tampered with'] - -It is used to keep track of how many form instances are being displayed. If -you are adding new forms via javascript, you should increment the count fields -in this form as well. - -Custom formset validation -~~~~~~~~~~~~~~~~~~~~~~~~~ - -A formset has a ``clean`` method similar to the one on a ``Form`` class. This -is where you define your own validation that deals at the formset level:: - - >>> from django.newforms.formsets import BaseFormSet - - >>> class BaseArticleFormSet(BaseFormSet): - ... def clean(self): - ... raise forms.ValidationError, u'An error occured.' - - >>> ArticleFormSet = formset_factory(ArticleForm, formset=BaseArticleFormSet) - >>> formset = ArticleFormSet({}) - >>> formset.is_valid() - False - >>> formset.non_form_errors() - [u'An error occured.'] - -The formset ``clean`` method is called after all the ``Form.clean`` methods -have been called. The errors will be found using the ``non_form_errors()`` -method on the formset. - -Dealing with ordering and deletion of forms -------------------------------------------- - -Common use cases with a formset is dealing with ordering and deletion of the -form instances. This has been dealt with for you. The ``formset_factory`` -provides two optional parameters ``can_order`` and ``can_delete`` that will do -the extra work of adding the extra fields and providing simpler ways of -getting to that data. - -``can_order`` -~~~~~~~~~~~~~ - -Default: ``False`` - -Lets create a formset with the ability to order:: - - >>> ArticleFormSet = formset_factory(ArticleForm, can_order=True) - >>> formset = ArticleFormSet(initial=[ - ... {'title': u'Article #1', 'pub_date': datetime.date(2008, 5, 10)}, - ... {'title': u'Article #2', 'pub_date': datetime.date(2008, 5, 11)}, - ... ]) - >>> for form in formset.forms: - ... print form.as_table() - <tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" value="Article #1" id="id_form-0-title" /></td></tr> - <tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" value="2008-05-10" id="id_form-0-pub_date" /></td></tr> - <tr><th><label for="id_form-0-ORDER">Order:</label></th><td><input type="text" name="form-0-ORDER" value="1" id="id_form-0-ORDER" /></td></tr> - <tr><th><label for="id_form-1-title">Title:</label></th><td><input type="text" name="form-1-title" value="Article #2" id="id_form-1-title" /></td></tr> - <tr><th><label for="id_form-1-pub_date">Pub date:</label></th><td><input type="text" name="form-1-pub_date" value="2008-05-11" id="id_form-1-pub_date" /></td></tr> - <tr><th><label for="id_form-1-ORDER">Order:</label></th><td><input type="text" name="form-1-ORDER" value="2" id="id_form-1-ORDER" /></td></tr> - <tr><th><label for="id_form-2-title">Title:</label></th><td><input type="text" name="form-2-title" id="id_form-2-title" /></td></tr> - <tr><th><label for="id_form-2-pub_date">Pub date:</label></th><td><input type="text" name="form-2-pub_date" id="id_form-2-pub_date" /></td></tr> - <tr><th><label for="id_form-2-ORDER">Order:</label></th><td><input type="text" name="form-2-ORDER" id="id_form-2-ORDER" /></td></tr> - -This adds an additional field to each form. This new field is named ``ORDER`` -and is an ``forms.IntegerField``. For the forms that came from the initial -data it automatically assigned them a numeric value. Lets look at what will -happen when the user changes these values:: - - >>> data = { - ... 'form-TOTAL_FORMS': u'3', - ... 'form-INITIAL_FORMS': u'2', - ... 'form-0-title': u'Article #1', - ... 'form-0-pub_date': u'2008-05-10', - ... 'form-0-ORDER': u'2', - ... 'form-1-title': u'Article #2', - ... 'form-1-pub_date': u'2008-05-11', - ... 'form-1-ORDER': u'1', - ... 'form-2-title': u'Article #3', - ... 'form-2-pub_date': u'2008-05-01', - ... 'form-2-ORDER': u'0', - ... } - - >>> formset = ArticleFormSet(data, initial=[ - ... {'title': u'Article #1', 'pub_date': datetime.date(2008, 5, 10)}, - ... {'title': u'Article #2', 'pub_date': datetime.date(2008, 5, 11)}, - ... ]) - >>> formset.is_valid() - True - >>> for form in formset.ordered_forms: - ... print form.cleaned_data - {'pub_date': datetime.date(2008, 5, 1), 'ORDER': 0, 'title': u'Article #3'} - {'pub_date': datetime.date(2008, 5, 11), 'ORDER': 1, 'title': u'Article #2'} - {'pub_date': datetime.date(2008, 5, 10), 'ORDER': 2, 'title': u'Article #1'} - -``can_delete`` -~~~~~~~~~~~~~~ - -Default: ``False`` - -Lets create a formset with the ability to delete:: - - >>> ArticleFormSet = formset_factory(ArticleForm, can_delete=True) - >>> formset = ArticleFormSet(initial=[ - ... {'title': u'Article #1', 'pub_date': datetime.date(2008, 5, 10)}, - ... {'title': u'Article #2', 'pub_date': datetime.date(2008, 5, 11)}, - ... ]) - >>> for form in formset.forms: - .... print form.as_table() - <input type="hidden" name="form-TOTAL_FORMS" value="3" id="id_form-TOTAL_FORMS" /><input type="hidden" name="form-INITIAL_FORMS" value="2" id="id_form-INITIAL_FORMS" /> - <tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" value="Article #1" id="id_form-0-title" /></td></tr> - <tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" value="2008-05-10" id="id_form-0-pub_date" /></td></tr> - <tr><th><label for="id_form-0-DELETE">Delete:</label></th><td><input type="checkbox" name="form-0-DELETE" id="id_form-0-DELETE" /></td></tr> - <tr><th><label for="id_form-1-title">Title:</label></th><td><input type="text" name="form-1-title" value="Article #2" id="id_form-1-title" /></td></tr> - <tr><th><label for="id_form-1-pub_date">Pub date:</label></th><td><input type="text" name="form-1-pub_date" value="2008-05-11" id="id_form-1-pub_date" /></td></tr> - <tr><th><label for="id_form-1-DELETE">Delete:</label></th><td><input type="checkbox" name="form-1-DELETE" id="id_form-1-DELETE" /></td></tr> - <tr><th><label for="id_form-2-title">Title:</label></th><td><input type="text" name="form-2-title" id="id_form-2-title" /></td></tr> - <tr><th><label for="id_form-2-pub_date">Pub date:</label></th><td><input type="text" name="form-2-pub_date" id="id_form-2-pub_date" /></td></tr> - <tr><th><label for="id_form-2-DELETE">Delete:</label></th><td><input type="checkbox" name="form-2-DELETE" id="id_form-2-DELETE" /></td></tr> - -Similar to ``can_order`` this adds a new field to each form named ``DELETE`` -and is a ``forms.BooleanField``. When data comes through marking any of the -delete fields you can access them with ``deleted_forms``:: - - >>> data = { - ... 'form-TOTAL_FORMS': u'3', - ... 'form-INITIAL_FORMS': u'2', - ... 'form-0-title': u'Article #1', - ... 'form-0-pub_date': u'2008-05-10', - ... 'form-0-DELETE': u'on', - ... 'form-1-title': u'Article #2', - ... 'form-1-pub_date': u'2008-05-11', - ... 'form-1-DELETE': u'', - ... 'form-2-title': u'', - ... 'form-2-pub_date': u'', - ... 'form-2-DELETE': u'', - ... } - - >>> formset = ArticleFormSet(data, initial=[ - ... {'title': u'Article #1', 'pub_date': datetime.date(2008, 5, 10)}, - ... {'title': u'Article #2', 'pub_date': datetime.date(2008, 5, 11)}, - ... ]) - >>> [form.cleaned_data for form in formset.deleted_forms] - [{'DELETE': True, 'pub_date': datetime.date(2008, 5, 10), 'title': u'Article #1'}] - -Adding additional fields to a formset -------------------------------------- - -If you need to add additional fields to the formset this can be easily -accomplished. The formset base class provides an ``add_fields`` method. You -can simply override this method to add your own fields or even redefine the -default fields/attributes of the order and deletion fields:: - - >>> class BaseArticleFormSet(BaseFormSet): - ... def add_fields(self, form, index): - ... super(BaseArticleFormSet, self).add_fields(form, index) - ... form.fields["my_field"] = forms.CharField() - - >>> ArticleFormSet = formset_factory(ArticleForm, formset=BaseArticleFormSet) - >>> formset = ArticleFormSet() - >>> for form in formset.forms: - ... print form.as_table() - <tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" id="id_form-0-title" /></td></tr> - <tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" id="id_form-0-pub_date" /></td></tr> - <tr><th><label for="id_form-0-my_field">My field:</label></th><td><input type="text" name="form-0-my_field" id="id_form-0-my_field" /></td></tr> - -Using a formset in views and templates --------------------------------------- - -Using a formset inside a view is as easy as using a regular ``Form`` class. -The only thing you will want to be aware of is making sure to use the -management form inside the template. Lets look at a sample view:: - - def manage_articles(request): - ArticleFormSet = formset_factory(ArticleForm) - if request.method == 'POST': - formset = ArticleFormSet(request.POST, request.FILES) - if formset.is_valid(): - # do something with the formset.cleaned_data - else: - formset = ArticleFormSet() - return render_to_response('manage_articles.html', {'formset': formset}) - -The ``manage_articles.html`` template might look like this:: - - <form method="POST" action=""> - {{ formset.management_form }} - <table> - {% for form in formset.forms %} - {{ form }} - {% endfor %} - </table> - </form> - -However the above can be slightly shortcutted and let the formset itself deal -with the management form:: - - <form method="POST" action=""> - <table> - {{ formset }} - </table> - </form> - -The above ends up calling the ``as_table`` method on the formset class. - -More coming soon -================ - -That's all the documentation for now. For more, see the file -http://code.djangoproject.com/browser/django/trunk/tests/regressiontests/forms --- the unit tests for ``django.newforms``. This can give you a good idea of -what's possible. (Each submodule there contains separate tests.) - -If you're really itching to learn and use this library, please be patient. -We're working hard on finishing both the code and documentation. diff --git a/docs/oldforms.txt b/docs/oldforms.txt new file mode 100644 index 0000000000..7703483f5d --- /dev/null +++ b/docs/oldforms.txt @@ -0,0 +1,696 @@ +=============================== +Forms, fields, and manipulators +=============================== + +Forwards-compatibility note +=========================== + +The legacy forms/manipulators system described in this document is going to be +replaced in the next Django release. If you're starting from scratch, we +strongly encourage you not to waste your time learning this. Instead, learn and +use the new `forms library`_. + +.. _forms library: ../forms/ + +Introduction +============ + +Once you've got a chance to play with Django's admin interface, you'll probably +wonder if the fantastic form validation framework it uses is available to user +code. It is, and this document explains how the framework works. + +We'll take a top-down approach to examining Django's form validation framework, +because much of the time you won't need to use the lower-level APIs. Throughout +this document, we'll be working with the following model, a "place" object:: + + from django.db import models + + PLACE_TYPES = ( + (1, 'Bar'), + (2, 'Restaurant'), + (3, 'Movie Theater'), + (4, 'Secret Hideout'), + ) + + class Place(models.Model): + 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(max_length=5, blank=True) + place_type = models.IntegerField(choices=PLACE_TYPES) + + class Admin: + pass + + def __unicode__(self): + return self.name + +Defining the above class is enough to create an admin interface to a ``Place``, +but what if you want to allow public users to submit places? + +Automatic Manipulators +====================== + +The highest-level interface for object creation and modification is the +**automatic Manipulator** framework. An automatic manipulator is a utility +class tied to a given model that "knows" how to create or modify instances of +that model and how to validate data for the object. Automatic Manipulators come +in two flavors: ``AddManipulators`` and ``ChangeManipulators``. Functionally +they are quite similar, but the former knows how to create new instances of the +model, while the latter modifies existing instances. Both types of classes are +automatically created when you define a new class:: + + >>> from mysite.myapp.models import Place + >>> Place.AddManipulator + <class 'django.models.manipulators.AddManipulator'> + >>> Place.ChangeManipulator + <class 'django.models.manipulators.ChangeManipulator'> + +Using the ``AddManipulator`` +---------------------------- + +We'll start with the ``AddManipulator``. Here's a very simple view that takes +POSTed data from the browser and creates a new ``Place`` object:: + + from django.shortcuts import render_to_response + from django.http import Http404, HttpResponse, HttpResponseRedirect + from django import oldforms as forms + from mysite.myapp.models import Place + + def naive_create_place(request): + """A naive approach to creating places; don't actually use this!""" + # Create the AddManipulator. + manipulator = Place.AddManipulator() + + # Make a copy of the POSTed data so that do_html2python can + # modify it in place (request.POST is immutable). + new_data = request.POST.copy() + + # Convert the request data (which will all be strings) into the + # appropriate Python types for those fields. + manipulator.do_html2python(new_data) + + # Save the new object. + new_place = manipulator.save(new_data) + + # It worked! + return HttpResponse("Place created: %s" % new_place) + +The ``naive_create_place`` example works, but as you probably can tell, this +view has a number of problems: + + * No validation of any sort is performed. If, for example, the ``name`` field + isn't given in ``request.POST``, the save step will cause a database error + because that field is required. Ugly. + + * Even if you *do* perform validation, there's still no way to give that + information to the user in any sort of useful way. + + * You'll have to separately create a form (and view) that submits to this + page, which is a pain and is redundant. + +Let's dodge these problems momentarily to take a look at how you could create a +view with a form that submits to this flawed creation view:: + + def naive_create_place_form(request): + """Simplistic place form view; don't actually use anything like this!""" + # Create a FormWrapper object that the template can use. Ignore + # the last two arguments to FormWrapper for now. + form = forms.FormWrapper(Place.AddManipulator(), {}, {}) + return render_to_response('places/naive_create_form.html', {'form': form}) + +(This view, as well as all the following ones, has the same imports as in the +first example above.) + +The ``forms.FormWrapper`` object is a wrapper that templates can +easily deal with to create forms. Here's the ``naive_create_form.html`` +template:: + + {% extends "base.html" %} + + {% block content %} + <h1>Create a place:</h1> + + <form method="post" action="../do_new/"> + <p><label for="id_name">Name:</label> {{ form.name }}</p> + <p><label for="id_address">Address:</label> {{ form.address }}</p> + <p><label for="id_city">City:</label> {{ form.city }}</p> + <p><label for="id_state">State:</label> {{ form.state }}</p> + <p><label for="id_zip_code">Zip:</label> {{ form.zip_code }}</p> + <p><label for="id_place_type">Place type:</label> {{ form.place_type }}</p> + <input type="submit" /> + </form> + {% endblock %} + +Before we get back to the problems with these naive set of views, let's go over +some salient points of the above template: + + * Field "widgets" are handled for you: ``{{ form.field }}`` automatically + creates the "right" type of widget for the form, as you can see with the + ``place_type`` field above. + + * There isn't a way just to spit out the form. You'll still need to define + how the form gets laid out. This is a feature: Every form should be + designed differently. Django doesn't force you into any type of mold. + If you must use tables, use tables. If you're a semantic purist, you can + probably find better HTML than in the above template. + + * To avoid name conflicts, the ``id`` values of form elements take the + form "id_*fieldname*". + +By creating a creation form we've solved problem number 3 above, but we still +don't have any validation. Let's revise the validation issue by writing a new +creation view that takes validation into account:: + + def create_place_with_validation(request): + manipulator = Place.AddManipulator() + new_data = request.POST.copy() + + # Check for validation errors + errors = manipulator.get_validation_errors(new_data) + manipulator.do_html2python(new_data) + if errors: + return render_to_response('places/errors.html', {'errors': errors}) + else: + new_place = manipulator.save(new_data) + return HttpResponse("Place created: %s" % new_place) + +In this new version, errors will be found -- ``manipulator.get_validation_errors`` +handles all the validation for you -- and those errors can be nicely presented +on an error page (templated, of course):: + + {% extends "base.html" %} + + {% block content %} + + <h1>Please go back and correct the following error{{ errors|pluralize }}:</h1> + <ul> + {% for e in errors.items %} + <li>Field "{{ e.0 }}": {{ e.1|join:", " }}</li> + {% endfor %} + </ul> + + {% endblock %} + +Still, this has its own problems: + + * There's still the issue of creating a separate (redundant) view for the + submission form. + + * Errors, though nicely presented, are on a separate page, so the user will + have to use the "back" button to fix errors. That's ridiculous and unusable. + +The best way to deal with these issues is to collapse the two views -- the form +and the submission -- into a single view. This view will be responsible for +creating the form, validating POSTed data, and creating the new object (if the +data is valid). An added bonus of this approach is that errors and the form will +both be available on the same page, so errors with fields can be presented in +context. + +.. admonition:: Philosophy: + + Finally, for the HTTP purists in the audience (and the authorship), this + nicely matches the "true" meanings of HTTP GET and HTTP POST: GET fetches + the form, and POST creates the new object. + +Below is the finished view:: + + def create_place(request): + manipulator = Place.AddManipulator() + + if request.method == 'POST': + # If data was POSTed, we're trying to create a new Place. + new_data = request.POST.copy() + + # Check for errors. + errors = manipulator.get_validation_errors(new_data) + manipulator.do_html2python(new_data) + + if not errors: + # No errors. This means we can save the data! + new_place = manipulator.save(new_data) + + # Redirect to the object's "edit" page. Always use a redirect + # after POST data, so that reloads don't accidently create + # duplicate entires, and so users don't see the confusing + # "Repost POST data?" alert box in their browsers. + return HttpResponseRedirect("/places/edit/%i/" % new_place.id) + else: + # No POST, so we want a brand new form without any data or errors. + errors = new_data = {} + + # Create the FormWrapper, template, context, response. + form = forms.FormWrapper(manipulator, new_data, errors) + return render_to_response('places/create_form.html', {'form': form}) + +and here's the ``create_form`` template:: + + {% extends "base.html" %} + + {% block content %} + <h1>Create a place:</h1> + + {% if form.has_errors %} + <h2>Please correct the following error{{ form.error_dict|pluralize }}:</h2> + {% endif %} + + <form method="post" action="."> + <p> + <label for="id_name">Name:</label> {{ form.name }} + {% if form.name.errors %}*** {{ form.name.errors|join:", " }}{% endif %} + </p> + <p> + <label for="id_address">Address:</label> {{ form.address }} + {% if form.address.errors %}*** {{ form.address.errors|join:", " }}{% endif %} + </p> + <p> + <label for="id_city">City:</label> {{ form.city }} + {% if form.city.errors %}*** {{ form.city.errors|join:", " }}{% endif %} + </p> + <p> + <label for="id_state">State:</label> {{ form.state }} + {% if form.state.errors %}*** {{ form.state.errors|join:", " }}{% endif %} + </p> + <p> + <label for="id_zip_code">Zip:</label> {{ form.zip_code }} + {% if form.zip_code.errors %}*** {{ form.zip_code.errors|join:", " }}{% endif %} + </p> + <p> + <label for="id_place_type">Place type:</label> {{ form.place_type }} + {% if form.place_type.errors %}*** {{ form.place_type.errors|join:", " }}{% endif %} + </p> + <input type="submit" /> + </form> + {% endblock %} + +The second two arguments to ``FormWrapper`` (``new_data`` and ``errors``) +deserve some mention. + +The first is any "default" data to be used as values for the fields. Pulling +the data from ``request.POST``, as is done above, makes sure that if there are +errors, the values the user put in aren't lost. If you try the above example, +you'll see this in action. + +The second argument is the error list retrieved from +``manipulator.get_validation_errors``. When passed into the ``FormWrapper``, +this gives each field an ``errors`` item (which is a list of error messages +associated with the field) as well as a ``html_error_list`` item, which is a +``<ul>`` of error messages. The above template uses these error items to +display a simple error message next to each field. The error list is saved as +an ``error_dict`` attribute of the ``FormWrapper`` object. + +Using the ``ChangeManipulator`` +------------------------------- + +The above has covered using the ``AddManipulator`` to create a new object. What +about editing an existing one? It's shockingly similar to creating a new one:: + + def edit_place(request, place_id): + # Get the place in question from the database and create a + # ChangeManipulator at the same time. + try: + manipulator = Place.ChangeManipulator(place_id) + except Place.DoesNotExist: + raise Http404 + + # Grab the Place object in question for future use. + place = manipulator.original_object + + if request.method == 'POST': + new_data = request.POST.copy() + errors = manipulator.get_validation_errors(new_data) + manipulator.do_html2python(new_data) + if not errors: + manipulator.save(new_data) + + # Do a post-after-redirect so that reload works, etc. + return HttpResponseRedirect("/places/edit/%i/" % place.id) + else: + errors = {} + # This makes sure the form accurate represents the fields of the place. + new_data = manipulator.flatten_data() + + form = forms.FormWrapper(manipulator, new_data, errors) + return render_to_response('places/edit_form.html', {'form': form, 'place': place}) + +The only real differences are: + + * We create a ``ChangeManipulator`` instead of an ``AddManipulator``. + The argument to a ``ChangeManipulator`` is the ID of the object + to be changed. As you can see, the initializer will raise an + ``ObjectDoesNotExist`` exception if the ID is invalid. + + * ``ChangeManipulator.original_object`` stores the instance of the + object being edited. + + * We set ``new_data`` based upon ``flatten_data()`` from the manipulator. + ``flatten_data()`` takes the data from the original object under + manipulation, and converts it into a data dictionary that can be used + to populate form elements with the existing values for the object. + + * The above example uses a different template, so create and edit can be + "skinned" differently if needed, but the form chunk itself is completely + identical to the one in the create form above. + +The astute programmer will notice the add and create functions are nearly +identical and could in fact be collapsed into a single view. This is left as an +exercise for said programmer. + +(However, the even-more-astute programmer will take heed of the note at the top +of this document and check out the `generic views`_ documentation if all she +wishes to do is this type of simple create/update.) + +Custom forms and manipulators +============================= + +All the above is fine and dandy if you just want to use the automatically +created manipulators. But the coolness doesn't end there: You can easily create +your own custom manipulators for handling custom forms. + +Custom manipulators are pretty simple. Here's a manipulator that you might use +for a "contact" form on a website:: + + from django import oldforms as forms + + urgency_choices = ( + (1, "Extremely urgent"), + (2, "Urgent"), + (3, "Normal"), + (4, "Unimportant"), + ) + + class ContactManipulator(forms.Manipulator): + def __init__(self): + self.fields = ( + forms.EmailField(field_name="from", 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), + ) + +A certain similarity to Django's models should be apparent. The only required +method of a custom manipulator is ``__init__`` which must define the fields +present in the manipulator. See the ``django.forms`` module for +all the form fields provided by Django. + +You use this custom manipulator exactly as you would use an auto-generated one. +Here's a simple function that might drive the above form:: + + def contact_form(request): + manipulator = ContactManipulator() + if request.method == 'POST': + new_data = request.POST.copy() + errors = manipulator.get_validation_errors(new_data) + manipulator.do_html2python(new_data) + if not errors: + + # Send e-mail using new_data here... + + return HttpResponseRedirect("/contact/thankyou/") + else: + errors = new_data = {} + form = forms.FormWrapper(manipulator, new_data, errors) + return render_to_response('contact_form.html', {'form': form}) + +Implementing ``flatten_data`` for custom manipulators +------------------------------------------------------ + +It is possible (although rarely needed) to replace the default automatically +created manipulators on a model with your own custom manipulators. If you do +this and you are intending to use those models in generic views, you should +also define a ``flatten_data`` method in any ``ChangeManipulator`` replacement. +This should act like the default ``flatten_data`` and return a dictionary +mapping field names to their values, like so:: + + def flatten_data(self): + obj = self.original_object + return dict( + from = obj.from, + subject = obj.subject, + ... + ) + +In this way, your new change manipulator will act exactly like the default +version. + +``FileField`` and ``ImageField`` special cases +============================================== + +Dealing with ``FileField`` and ``ImageField`` objects is a little more +complicated. + +First, you'll need to make sure that your ``<form>`` element correctly defines +the ``enctype`` as ``"multipart/form-data"``, in order to upload files:: + + <form enctype="multipart/form-data" method="post" action="/foo/"> + +Next, you'll need to treat the field in the template slightly differently. A +``FileField`` or ``ImageField`` is represented by *two* HTML form elements. + +For example, given this field in a model:: + + photo = model.ImageField('/path/to/upload/location') + +You'd need to display two formfields in the template:: + + <p><label for="id_photo">Photo:</label> {{ form.photo }}{{ form.photo_file }}</p> + +The first bit (``{{ form.photo }}``) displays the currently-selected file, +while the second (``{{ form.photo_file }}``) actually contains the file upload +form field. Thus, at the validation layer you need to check the ``photo_file`` +key. + +Finally, in your view, make sure to access ``request.FILES``, rather than +``request.POST``, for the uploaded files. This is necessary because +``request.POST`` does not contain file-upload data. + +For example, following the ``new_data`` convention, you might do something like +this:: + + new_data = request.POST.copy() + new_data.update(request.FILES) + +Validators +========== + +One useful feature of manipulators is the automatic validation. Validation is +done using a simple validation API: A validator is a callable that raises a +``ValidationError`` if there's something wrong with the data. +``django.core.validators`` defines a host of validator functions (see below), +but defining your own couldn't be easier:: + + from django.core import validators + from django import oldforms as forms + + class ContactManipulator(forms.Manipulator): + def __init__(self): + self.fields = ( + # ... snip fields as above ... + forms.EmailField(field_name="to", validator_list=[self.isValidToAddress]) + ) + + def isValidToAddress(self, field_data, all_data): + if not field_data.endswith("@example.com"): + raise validators.ValidationError("You can only send messages to example.com e-mail addresses.") + +Above, we've added a "to" field to the contact form, but required that the "to" +address end with "@example.com" by adding the ``isValidToAddress`` validator to +the field's ``validator_list``. + +The arguments to a validator function take a little explanation. ``field_data`` +is the value of the field in question, and ``all_data`` is a dictionary of all +the data being validated. + +.. admonition:: Note:: + + At the point validators are called all data will still be + strings (as ``do_html2python`` hasn't been called yet). + +Also, because consistency in user interfaces is important, we strongly urge you +to put punctuation at the end of your validation messages. + +When are validators called? +--------------------------- + +After a form has been submitted, Django validates each field in turn. First, +if the field is required, Django checks that it is present and non-empty. Then, +if that test passes *and the form submission contained data* for that field, all +the validators for that field are called in turn. The emphasized portion in the +last sentence is important: if a form field is not submitted (because it +contains no data -- which is normal HTML behavior), the validators are not +run against the field. + +This feature is particularly important for models using +``models.BooleanField`` or custom manipulators using things like +``forms.CheckBoxField``. If the checkbox is not selected, it will not +contribute to the form submission. + +If you would like your validator to run *always*, regardless of whether its +attached field contains any data, set the ``always_test`` attribute on the +validator function. For example:: + + def my_custom_validator(field_data, all_data): + # ... + my_custom_validator.always_test = True + +This validator will always be executed for any field it is attached to. + +Ready-made validators +--------------------- + +Writing your own validator is not difficult, but there are some situations +that come up over and over again. Django comes with a number of validators +that can be used directly in your code. All of these functions and classes +reside in ``django/core/validators.py``. + +The following validators should all be self-explanatory. Each one provides a +check for the given property: + + * isAlphaNumeric + * isAlphaNumericURL + * isSlug + * isLowerCase + * isUpperCase + * isCommaSeparatedIntegerList + * isCommaSeparatedEmailList + * isValidIPAddress4 + * isNotEmpty + * isOnlyDigits + * isNotOnlyDigits + * isInteger + * isOnlyLetters + * isValidANSIDate + * isValidANSITime + * isValidEmail + * isValidFloat + * isValidImage + * isValidImageURL + * isValidPhone + * isValidQuicktimeVideoURL + * isValidURL + * isValidHTML + * isWellFormedXml + * isWellFormedXmlFragment + * isExistingURL + * isValidUSState + * hasNoProfanities + +There are also a group of validators that are slightly more flexible. For +these validators, you create a validator instance, passing in the parameters +described below. The returned object is a callable that can be used as a +validator. + +For example:: + + from django.core import validators + from django import oldforms as forms + + power_validator = validators.IsAPowerOf(2) + + class InstallationManipulator(forms.Manipulator) + def __init__(self): + self.fields = ( + ... + forms.IntegerField(field_name = "size", validator_list=[power_validator]) + ) + +Here, ``validators.IsAPowerOf(...)`` returned something that could be used as +a validator (in this case, a check that a number was a power of 2). + +Each of the standard validators that take parameters have an optional final +argument (``error_message``) that is the message returned when validation +fails. If no message is passed in, a default message is used. + +``AlwaysMatchesOtherField`` + Takes a field name and the current field is valid if and only if its value + matches the contents of the other field. + +``ValidateIfOtherFieldEquals`` + Takes three parameters: ``other_field``, ``other_value`` and + ``validator_list``, in that order. If ``other_field`` has a value of + ``other_value``, then the validators in ``validator_list`` are all run + against the current field. + +``RequiredIfOtherFieldGiven`` + Takes a field name of the current field is only required if the other + field has a value. + +``RequiredIfOtherFieldsGiven`` + Similar to ``RequiredIfOtherFieldGiven``, except that it takes a list of + field names and if any one of the supplied fields has a value provided, + the current field being validated is required. + +``RequiredIfOtherFieldNotGiven`` + Takes the name of the other field and this field is only required if the + other field has no value. + +``RequiredIfOtherFieldEquals`` and ``RequiredIfOtherFieldDoesNotEqual`` + Each of these validator classes takes a field name and a value (in that + order). If the given field does (or does not have, in the latter case) the + given value, then the current field being validated is required. + + An optional ``other_label`` argument can be passed which, if given, is used + in error messages instead of the value. This allows more user friendly error + messages if the value itself is not descriptive enough. + + Note that because validators are called before any ``do_html2python()`` + functions, the value being compared against is a string. So + ``RequiredIfOtherFieldEquals('choice', '1')`` is correct, whilst + ``RequiredIfOtherFieldEquals('choice', 1)`` will never result in the + equality test succeeding. + +``IsLessThanOtherField`` + Takes a field name and validates that the current field being validated + has a value that is less than (or equal to) the other field's value. + Again, comparisons are done using strings, so be cautious about using + this function to compare data that should be treated as another type. The + string "123" is less than the string "2", for example. If you don't want + string comparison here, you will need to write your own validator. + +``NumberIsInRange`` + Takes two boundary numbers, ``lower`` and ``upper``, and checks that the + field is greater than ``lower`` (if given) and less than ``upper`` (if + given). + + Both checks are inclusive. That is, ``NumberIsInRange(10, 20)`` will allow + values of both 10 and 20. This validator only checks numeric values + (e.g., float and integer values). + +``IsAPowerOf`` + Takes an integer argument and when called as a validator, checks that the + field being validated is a power of the integer. + +``IsValidDecimal`` + Takes a maximum number of digits and number of decimal places (in that + order) and validates whether the field is a decimal with no more than the + maximum number of digits and decimal places. + +``MatchesRegularExpression`` + Takes a regular expression (a string) as a parameter and validates the + field value against it. + +``AnyValidator`` + Takes a list of validators as a parameter. At validation time, if the + field successfully validates against any one of the validators, it passes + validation. The validators are tested in the order specified in the + original list. + +``URLMimeTypeCheck`` + Used to validate URL fields. Takes a list of MIME types (such as + ``text/plain``) at creation time. At validation time, it verifies that the + field is indeed a URL and then tries to retrieve the content at the URL. + Validation succeeds if the content could be retrieved and it has a content + type from the list used to create the validator. + +``RelaxNGCompact`` + Used to validate an XML document against a Relax NG compact schema. Takes + a file path to the location of the schema and an optional root element + (which is wrapped around the XML fragment before validation, if supplied). + At validation time, the XML fragment is validated against the schema using + the executable specified in the ``JING_PATH`` setting (see the settings_ + document for more details). + +.. _`generic views`: ../generic_views/ +.. _`models API`: ../model-api/ +.. _settings: ../settings/ diff --git a/docs/overview.txt b/docs/overview.txt index 0fd1be5c85..dae0ffbd76 100644 --- a/docs/overview.txt +++ b/docs/overview.txt @@ -33,7 +33,7 @@ quick example:: class Article(models.Model): pub_date = models.DateTimeField() headline = models.CharField(max_length=200) - article = models.TextField() + content = models.TextField() reporter = models.ForeignKey(Reporter) def __unicode__(self): @@ -95,7 +95,7 @@ is created on the fly, no code generation necessary:: # Create an article. >>> from datetime import datetime >>> a = Article(pub_date=datetime.now(), headline='Django is cool', - ... article='Yeah.', reporter=r) + ... content='Yeah.', reporter=r) >>> a.save() # Now the article is in the database. @@ -135,7 +135,7 @@ your model classes:: class Article(models.Model): pub_date = models.DateTimeField() headline = models.CharField(max_length=200) - article = models.TextField() + content = models.TextField() reporter = models.ForeignKey(Reporter) class Admin: pass diff --git a/docs/pagination.txt b/docs/pagination.txt index bfdf8eea1c..c36f56244f 100644 --- a/docs/pagination.txt +++ b/docs/pagination.txt @@ -59,28 +59,65 @@ page:: ... InvalidPage -Note that you can give ``Paginator`` a list/tuple or a Django ``QuerySet``. The -only difference is in implementation; if you pass a ``QuerySet``, the -``Paginator`` will call its ``count()`` method instead of using ``len()``, -because the former is more efficient. - ``Paginator`` objects ===================== +Required arguments +------------------ + +``object_list`` + A list, tuple, Django ``QuerySet``, or other sliceable object with a + ``count()`` or ``__len__()`` method. + +``per_page`` + The maximum number of items to include on a page, not including orphans + (see the ``orphans`` optional argument below). + +Optional arguments +------------------ + +``orphans`` + The minimum number of items allowed on the last page, defaults to zero. + Use this when you don't want to have a last page with very few items. + If the last page would normally have a number of items less than or equal + to ``orphans``, then those items will be added to the previous page (which + becomes the last page) instead of leaving the items on a page by + themselves. For example, with 23 items, ``per_page=10``, and + ``orphans=3``, there will be two pages; the first page with 10 items and + the second (and last) page with 13 items. + +``allow_empty_first_page`` + Whether or not the first page is allowed to be empty. If ``False`` and + ``object_list`` is empty, then a ``EmptyPage`` error will be raised. + Methods ------- -``page(number)`` -- Returns a ``Page`` object with the given 1-based index. -Raises ``InvalidPage`` if the given page number doesn't exist. +``page(number)`` + Returns a ``Page`` object with the given 1-based index. Raises + ``InvalidPage`` if the given page number doesn't exist. Attributes ---------- -``count`` -- The total number of objects, across all pages. +In addition to the arguments above, which get stored as attributes, a +``Paginator`` object also has the following attributes: + +``count`` + The total number of objects, across all pages. + + **Note**: When determining the number of objects contained in + ``object_list``, ``Paginator`` will first try calling + ``object_list.count()``. If ``object_list`` has no ``count()`` method, then + ``Paginator`` will fallback to using ``object_list.__len__()``. This allows + objects, such as Django's ``QuerySet``, to use a more efficient ``count()`` + method when available. -``num_pages`` -- The total number of pages. +``num_pages`` + The total number of pages. -``page_range`` -- A 1-based range of page numbers, e.g., ``[1, 2, 3, 4]``. +``page_range`` + A 1-based range of page numbers, e.g., ``[1, 2, 3, 4]``. ``InvalidPage`` exceptions ========================== @@ -90,9 +127,12 @@ The ``page()`` method raises ``InvalidPage`` if the requested page is invalid the ``InvalidPage`` exception, but if you'd like more granularity, you can trap either of the following exceptions: -``PageNotAnInteger`` -- Raised when ``page()`` is given a value that isn't an integer. +``PageNotAnInteger`` + Raised when ``page()`` is given a value that isn't an integer. -``EmptyPage`` -- Raised when ``page()`` is given a valid value but no objects exist on that page. +``EmptyPage`` + Raised when ``page()`` is given a valid value but no objects exist on that + page. Both of the exceptions are subclasses of ``InvalidPage``, so you can handle them both with a simple ``except InvalidPage``. @@ -103,44 +143,43 @@ them both with a simple ``except InvalidPage``. Methods ------- -``has_next()`` -- Returns ``True`` if there's a next page. +``has_next()`` + Returns ``True`` if there's a next page. -``has_previous()`` -- Returns ``True`` if there's a previous page. +``has_previous()`` + Returns ``True`` if there's a previous page. -``has_other_pages()`` -- Returns ``True`` if there's a next *or* previous page. +``has_other_pages()`` + Returns ``True`` if there's a next *or* previous page. -``next_page_number()`` -- Returns the next page number. Note that this is -"dumb" and will return the next page number regardless of whether a subsequent -page exists. +``next_page_number()`` + Returns the next page number. Note that this is "dumb" and will return the + next page number regardless of whether a subsequent page exists. -``previous_page_number()`` -- Returns the previous page number. Note that this -is "dumb" and will return the previous page number regardless of whether a -previous page exists. +``previous_page_number()`` + Returns the previous page number. Note that this is "dumb" and will return + the previous page number regardless of whether a previous page exists. -``start_index()`` -- Returns the 1-based index of the first object on the page, -relative to all of the objects in the paginator's list. For example, when -paginating a list of 5 objects with 2 objects per page, the second page's -``start_index()`` would return ``3``. +``start_index()`` + Returns the 1-based index of the first object on the page, relative to all + of the objects in the paginator's list. For example, when paginating a list + of 5 objects with 2 objects per page, the second page's ``start_index()`` + would return ``3``. -``end_index()`` -- Returns the 1-based index of the last object on the page, -relative to all of the objects in the paginator's list. For example, when -paginating a list of 5 objects with 2 objects per page, the second page's -``end_index()`` would return ``4``. +``end_index()`` + Returns the 1-based index of the last object on the page, relative to all + of the objects in the paginator's list. For example, when paginating a list + of 5 objects with 2 objects per page, the second page's ``end_index()`` + would return ``4``. Attributes ---------- -``object_list`` -- The list of objects on this page. - -``number`` -- The 1-based page number for this page. - -``paginator`` -- The associated ``Paginator`` object. +``object_list`` + The list of objects on this page. -The legacy ``ObjectPaginator`` class -==================================== +``number`` + The 1-based page number for this page. -The ``Paginator`` and ``Page`` classes are new in the Django development -version, as of revision 7306. In previous versions, Django provided an -``ObjectPaginator`` class that offered similar functionality but wasn't as -convenient. This class still exists, for backwards compatibility, but Django -now issues a ``DeprecationWarning`` if you try to use it. +``paginator`` + The associated ``Paginator`` object. diff --git a/docs/release_notes_0.95.txt b/docs/release_notes_0.95.txt index f2ecbb66e6..a61b10d567 100644 --- a/docs/release_notes_0.95.txt +++ b/docs/release_notes_0.95.txt @@ -99,7 +99,7 @@ Problem reports and getting help ================================ Need help resolving a problem with Django? The documentation in the -distribution is also available online_ at the `Django website`_. The FAQ_ +distribution is also available online_ at the `Django Web site`_. The FAQ_ document is especially recommended, as it contains a number of issues that come up time and again. @@ -115,7 +115,7 @@ 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/0.95/ -.. _Django website: http://www.djangoproject.com/ +.. _Django Web site: http://www.djangoproject.com/ .. _FAQ: http://www.djangoproject.com/documentation/faq/ .. _django-users: http://groups.google.com/group/django-users diff --git a/docs/release_notes_1.0_alpha.txt b/docs/release_notes_1.0_alpha.txt new file mode 100644 index 0000000000..6fce66532e --- /dev/null +++ b/docs/release_notes_1.0_alpha.txt @@ -0,0 +1,163 @@ +================================ +Django 1.0 alpha release notes +================================ + +Welcome to Django 1.0 alpha! + +This is the first in a series of preview/development releases leading +up to the eventual release of Django 1.0, currently scheduled to take +place in early September 2008. This release is primarily targeted at +developers who are interested in testing the Django codebase and +helping to identify and resolve bugs prior to the final 1.0 release. + +As such, this release is *not* intended for production use, and any +such use is strongly discouraged. + + +What's new in Django 1.0 alpha +============================== + +Django's development trunk has been the site of nearly constant +activity over the past year, with several major new features landing +since the 0.96 release. Some of the highlights include: + +Refactored admin application (newforms-admin) + The Django administrative interface (``django.contrib.admin``) has + been completely refactored; admin definitions are now completely + decoupled from model definitions (no more ``class Admin`` + declaration in models!), rewritten to use Django's new + form-handling library (introduced in the 0.96 release as + ``django.newforms``, and now available as simply ``django.forms``) + and redesigned with extensibility and customization in mind. Full + documentation for the admin application is available online in the + official Django documentation: + + http://www.djangoproject.com/documentation/admin/ + +Improved Unicode handling + Django's internals have been refactored to use Unicode throughout; + this drastically simplifies the task of dealing with + non-Western-European content and data in Django. Additionally, + utility functions have been provided to ease interoperability with + third-party libraries and systems which may or may not handle + Unicode gracefully. Details are available in Django's + Unicode-handling documentation: + + http://www.djangoproject.com/documentation/unicode/ + +An improved Django ORM + Django's object-relational mapper -- the component which provides + the mapping between Django model classes and your database, and + which mediates your database queries -- has been dramatically + improved by a massive refactoring. For most users of Django this + is backwards-compatible; the public-facing API for database + querying underwent a few minor changes, but most of the updates + took place in the ORM's internals. A guide to the changes, + including backwards-incompatible modifications and mentions of new + features opened up by this refactoring, is available on the Django + wiki: + + http://code.djangoproject.com/wiki/QuerysetRefactorBranch + +Automatic escaping of template variables + To provide improved security against cross-site scripting (XSS) + vulnerabilities, Django's template system now automatically + escapes the output of variables. This behavior is configurable, + and allows both variables and larger template constructs to be + marked as safe (requiring no escaping) or unsafe (requiring + escaping). A full guide to this feature is in the documentation + for the Django template system: + + http://www.djangoproject.com/documentation/templates/#automatic-html-escaping + +There are many more new features, many bugfixes and many enhancements +to existing features from previous releases. The ``newforms`` library, +for example, has undergone massive improvements including several +useful add-ons in ``django.contrib`` which complement and build on +Django's form-handling capabilities, and Django's file-uploading +handlers have been refactored to allow finer-grained control over the +uploading process as well as streaming uploads of large files. + +Along with these improvements and additions, we've made a number of +of backwards-incompatible changes to the framework, as features have been +fleshed out and APIs have been finalized for the 1.0 release. A +complete guide to these changes will be available as part of the final +Django 1.0 release, and a comprehensive list of backwards-incompatible +changes is also available on the Django wiki for those who want to +begin developing and testing their upgrade process: + + http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges + + +The Django 1.0 roadmap +====================== + +One of the primary goals of this alpha release is to focus attention +on the remaining features to be implemented for Django 1.0, and on the +bugs that need to be resolved before the final release. Following +this release, we'll be conducting a series of sprints building up to a +series of beta releases and a release-candidate stage, followed soon +after by Django 1.0. The timeline is projected to be: + +* August 1, 2008: Sprint (based in Washington, DC, and online). + +* August 5, 2008: Django 1.0 beta 1 release. This will also constitute + the feature freeze for 1.0. Any feature to be included in 1.0 must + be completed and in trunk by this time. + +* August 8, 2008: Sprint (based in Lawrence, KS, and online). + +* August 12, 2008: Django 1.0 beta 2 release. + +* August 15, 2008: Sprint (based in Austin, TX, and online). + +* August 19, 2008: Django 1.0 release candidate 1. + +* August 22, 2008: Sprint (based in Portland, OR, and online). + +* August 26, 2008: Django 1.0 release candidate 2. + +* September 2, 2008: Django 1.0 final release. The official Django 1.0 + release party will take place during the first-ever DjangoCon, to be + held in Mountain View, CA, September 6-7. + +Of course, like any estimated timeline, this is subject to change as +requirements dictate. The latest information will always be available +on the Django project wiki: + + http://code.djangoproject.com/wiki/VersionOneRoadmap + + +What you can do to help +======================= + +In order to provide a high-quality 1.0 release, we need your +help. Although this alpha release is, again, *not* intended for +production use, you can help the Django team by trying out the alpha +codebase in a safe test environment and reporting any bugs or issues +you encounter. The Django ticket tracker is the central place to +search for open issues: + + http://code.djangoproject.com/timeline + +Please open new tickets if no existing ticket corresponds to a problem +you're running into. + +Additionally, discussion of Django development, including progress +toward the 1.0 release, takes place daily on the django-developers +mailing list: + + http://groups.google.com/group/django-developers + +...and in the ``#django-dev`` IRC channel on ``irc.freenode.net``. If +you're interested in helping out with Django's development, feel free +to join the discussions there. + +Django's online documentation also includes pointers on how to +contribute to Django: + + http://www.djangoproject.com/documentation/contributing/ + +Contributions on any level -- developing code, writing +documentation or simply triaging tickets and helping to test proposed +bugfixes -- are always welcome and appreciated. diff --git a/docs/request_response.txt b/docs/request_response.txt index 54fc24df9e..9b3f6dd0e3 100644 --- a/docs/request_response.txt +++ b/docs/request_response.txt @@ -170,18 +170,6 @@ All attributes except ``session`` should be considered read-only. Methods ------- -``__getitem__(key)`` - Returns the GET/POST value for the given key, checking POST first, then - GET. Raises ``KeyError`` if the key doesn't exist. - - This lets you use dictionary-accessing syntax on an ``HttpRequest`` - instance. Example: ``request["foo"]`` would return ``True`` if either - ``request.POST`` or ``request.GET`` had a ``"foo"`` key. - -``has_key()`` - Returns ``True`` or ``False``, designating whether ``request.GET`` or - ``request.POST`` has the given key. - ``get_host()`` **New in Django development version** diff --git a/docs/serialization.txt b/docs/serialization.txt index 2a3e7038da..971103747c 100644 --- a/docs/serialization.txt +++ b/docs/serialization.txt @@ -2,13 +2,6 @@ Serializing Django objects ========================== -.. note:: - - This API is currently under heavy development and may change -- - perhaps drastically -- in the future. - - You have been warned. - Django's serialization framework provides a mechanism for "translating" Django objects into other formats. Usually these other formats will be text-based and used for sending Django objects over a wire, but it's possible for a @@ -58,10 +51,10 @@ be serialized. .. note:: - Depending on your model, you may find that it is not possible to deserialize - a model that only serializes a subset of its fields. If a serialized object - doesn't specify all the fields that are required by a model, the deserializer - will not be able to save deserialized instances. + Depending on your model, you may find that it is not possible to + deserialize a model that only serializes a subset of its fields. If a + serialized object doesn't specify all the fields that are required by a + model, the deserializer will not be able to save deserialized instances. Inherited Models ~~~~~~~~~~~~~~~~ @@ -75,13 +68,13 @@ However, if you have a model that uses `multi-table inheritance`_, you also need to serialize all of the base classes for the model. This is because only the fields that are locally defined on the model will be serialized. For example, consider the following models:: - + class Place(models.Model): name = models.CharField(max_length=50) - + class Restaurant(Place): serves_hot_dogs = models.BooleanField() - + If you only serialize the Restaurant model:: data = serializers.serialize('xml', Restaurant.objects.all()) @@ -126,7 +119,8 @@ something like:: deserialized_object.save() In other words, the usual use is to examine the deserialized objects to make -sure that they are "appropriate" for saving before doing so. Of course, if you trust your data source you could just save the object and move on. +sure that they are "appropriate" for saving before doing so. Of course, if you +trust your data source you could just save the object and move on. The Django object itself can be inspected as ``deserialized_object.object``. diff --git a/docs/sessions.txt b/docs/sessions.txt index 648832b85d..b5c9ba8394 100644 --- a/docs/sessions.txt +++ b/docs/sessions.txt @@ -65,10 +65,10 @@ you've configured your cache; see the `cache documentation`_ for details. .. note:: You should probably only use cache-based sessions if you're using the - memcached cache backend. The local memory and simple cache backends - don't retain data long enough to be good choices, and it'll be faster - to use file or database sessions directly instead of sending everything - through the file or database cache backends. + Memcached cache backend. The local-memory cache backend doesn't retain data + long enough to be a good choice, and it'll be faster to use file or + database sessions directly instead of sending everything through the file + or database cache backends. Using sessions in views ======================= diff --git a/docs/settings.txt b/docs/settings.txt index fbe23b5e88..1d1627d41e 100644 --- a/docs/settings.txt +++ b/docs/settings.txt @@ -537,8 +537,8 @@ FILE_UPLOAD_HANDLERS Default:: - ("django.core.files.fileuploadhandler.MemoryFileUploadHandler", - "django.core.files.fileuploadhandler.TemporaryFileUploadHandler",) + ("django.core.files.uploadhandler.MemoryFileUploadHandler", + "django.core.files.uploadhandler.TemporaryFileUploadHandler",) A tuple of handlers to use for uploading. See `file uploads`_ for details. @@ -563,7 +563,7 @@ Default: ``None`` The directory to store data temporarily while uploading files. If ``None``, Django will use the standard temporary directory for the operating system. For -example, this will default to '/tmp' on *nix-style operating systems. +example, this will default to '/tmp' on \*nix-style operating systems. See `file uploads`_ for details. @@ -578,6 +578,16 @@ these paths should use Unix-style forward slashes, even on Windows. See .. _Testing Django Applications: ../testing/ +FORCE_SCRIPT_NAME +------------------ + +Default: ``None`` + +If not ``None``, this will be used as the value of the ``SCRIPT_NAME`` +environment variable in any HTTP request. This setting can be used to override +the server-provided value of ``SCRIPT_NAME``, which may be a rewritten version +of the preferred value or not supplied at all. + IGNORABLE_404_ENDS ------------------ diff --git a/docs/sitemaps.txt b/docs/sitemaps.txt index 6a16e61879..3e7411c168 100644 --- a/docs/sitemaps.txt +++ b/docs/sitemaps.txt @@ -282,6 +282,10 @@ This will automatically generate a ``sitemap.xml`` file that references both ``sitemap-flatpages.xml`` and ``sitemap-blog.xml``. The ``Sitemap`` classes and the ``sitemaps`` dict don't change at all. +If one of your sitemaps is going to have more than 50,000 URLs you should +create an index file. Your sitemap will be paginated and the index will +reflect that. + Pinging Google ============== diff --git a/docs/templates.txt b/docs/templates.txt index 04a46580c5..587c5915f0 100644 --- a/docs/templates.txt +++ b/docs/templates.txt @@ -703,9 +703,9 @@ You can loop over a list in reverse by using ``{% for obj in list reversed %}``. **New in Django development version** If you need to loop over a list of lists, you can unpack the values -in eachs sub-list into a set of known names. For example, if your context contains -a list of (x,y) coordinates called ``points``, you could use the following -to output the list of points:: +in each sub-list into individual variables. For example, if your context +contains a list of (x,y) coordinates called ``points``, you could use the +following to output the list of points:: {% for x, y in points %} There is a point at {{ x }},{{ y }} @@ -828,6 +828,19 @@ The 'ifchanged' block tag is used within a loop. It has two possible uses. {% endifchanged %} {% endfor %} +The ``ifchanged`` tag also takes an optional ``{% else %}`` clause that will +be displayed if the value has not changed:: + + {% for match in matches %} + <div style="background-color: + {% ifchanged match.ballot_id %} + {% cycle red,blue %} + {% else %} + grey + {% endifchanged %} + ">{{ match }}</div> + {% endfor %} + ifequal ~~~~~~~ @@ -1550,7 +1563,7 @@ For example:: {{ value|linebreaks }} -If ``value`` is ``Joel\nis a slug``, the output will be ``<p>Joe<br>is a +If ``value`` is ``Joel\nis a slug``, the output will be ``<p>Joel<br>is a slug</p>``. linebreaksbr @@ -1592,9 +1605,9 @@ For example:: {{ value|make_list }} -If ``value`` is the string ``"Joe"``, the output would be the list -``[u'J', u'o', u'e']``. If ``value`` is ``123``, the output will be the list -``[1, 2, 3]``. +If ``value`` is the string ``"Joel"``, the output would be the list +``[u'J', u'o', u'e', u'l']``. If ``value`` is ``123``, the output will be the +list ``[1, 2, 3]``. phone2numeric ~~~~~~~~~~~~~ @@ -1945,7 +1958,7 @@ information. django.contrib.webdesign ------------------------ -A collection of template tags that can be useful while designing a website, +A collection of template tags that can be useful while designing a Web site, such as a generator of Lorem Ipsum text. See the `webdesign documentation`_. .. _webdesign documentation: ../webdesign/ diff --git a/docs/testing.txt b/docs/testing.txt index bb091bfd6b..c9b23c2948 100644 --- a/docs/testing.txt +++ b/docs/testing.txt @@ -25,13 +25,6 @@ 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 - slightly before the next official Django release. - - (That's *no* excuse not to write tests, though!) - Writing tests ============= @@ -58,7 +51,7 @@ frameworks are: import unittest - class MyFuncTestCase(unittest.TestCase) + class MyFuncTestCase(unittest.TestCase): def testBasic(self): a = ['larry', 'curly', 'moe'] self.assertEquals(my_func(a, 0), 'larry') @@ -815,12 +808,12 @@ In order to provide a reliable URL space for your test, configuration for the duration of the execution of a test suite. If your ``TestCase`` instance defines an ``urls`` attribute, the ``TestCase`` will use the value of that attribute as the ``ROOT_URLCONF`` -for the duration of that test. +for the duration of that test. For example:: from django.test import TestCase - + class TestMyViews(TestCase): urls = 'myapp.test_urls' @@ -864,7 +857,7 @@ useful for testing Web applications: rendered on the form. ``form`` is the name the ``Form`` instance was given in the template - context. Note that this works only for ``newforms.Form`` instances, not + context. Note that this works only for ``forms.Form`` instances, not ``oldforms.Form`` instances. ``field`` is the name of the field on the form to check. If ``field`` diff --git a/docs/tutorial02.txt b/docs/tutorial02.txt index c69fd1459f..7ed04be643 100644 --- a/docs/tutorial02.txt +++ b/docs/tutorial02.txt @@ -29,12 +29,36 @@ The Django admin site is not activated by default -- it's an opt-in thing. To activate the admin site for your installation, do these three things: * Add ``"django.contrib.admin"`` to your ``INSTALLED_APPS`` setting. + * Run ``python manage.py syncdb``. Since you have added a new application to ``INSTALLED_APPS``, the database tables need to be updated. + * Edit your ``mysite/urls.py`` file and uncomment the lines below the "Uncomment this for admin:" comments. This file is a URLconf; we'll dig into URLconfs in the next tutorial. For now, all you need to know is that - it maps URL roots to applications. + it maps URL roots to applications. In the end, you should have a + ``urls.py`` file that looks like this: + + .. parsed-literal:: + + from django.conf.urls.defaults import * + + # Uncomment the next two lines to enable the admin: + **from django.contrib import admin** + **admin.autodiscover()** + + urlpatterns = patterns('', + # Example: + # (r'^{{ project_name }}/', include('{{ project_name }}.foo.urls')), + + # Uncomment the next line to enable admin documentation: + # (r'^admin/doc/', include('django.contrib.admindocs.urls')), + + # Uncomment the next line for to enable the admin: + **(r'^admin/(.*)', admin.site.root),** + ) + + (The bold lines are the ones that needed to be uncommented.) Start the development server ============================ @@ -192,7 +216,7 @@ aren't commonly used:: class PollAdmin(admin.ModelAdmin): fieldsets = [ (None, {'fields': ['question']}), - ('Date information', {'fields': ['pub_date'], 'classes': 'pub_date'}), + ('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}), ] .. image:: http://media.djangoproject.com/img/doc/tutorial-trunk/admin09.png @@ -242,7 +266,7 @@ registration code to read:: class PollAdmin(admin.ModelAdmin): fieldsets = [ (None, {'fields': ['question']}), - ('Date information', {'fields': ['pub_date'], 'classes': 'pub_date'}), + ('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}), ] inlines = [ChoiceInline] diff --git a/docs/tutorial03.txt b/docs/tutorial03.txt index d49a417dcf..2ac106229f 100644 --- a/docs/tutorial03.txt +++ b/docs/tutorial03.txt @@ -441,11 +441,11 @@ Here's what happens if a user goes to "/polls/34/" in this system: * Django will find the match at ``'^polls/'`` * It will strip off the matching text (``"polls/"``) and send the remaining - text -- ``"34/"`` -- to the 'mysite.polls.urls' urlconf for + text -- ``"34/"`` -- to the 'mysite.polls.urls' URLconf for further processing. Now that we've decoupled that, we need to decouple the -'mysite.polls.urls' urlconf by removing the leading "polls/" from each +'mysite.polls.urls' URLconf by removing the leading "polls/" from each line:: urlpatterns = patterns('mysite.polls.views', diff --git a/docs/upload_handling.txt b/docs/upload_handling.txt index e11d0d94f0..c0e8605686 100644 --- a/docs/upload_handling.txt +++ b/docs/upload_handling.txt @@ -17,7 +17,7 @@ Basic file uploads Consider a simple form containing a ``FileField``:: - from django import newforms as forms + from django import forms class UploadFileForm(forms.Form): title = forms.CharField(max_length=50) @@ -48,7 +48,7 @@ something like:: form = UploadFileForm() return render_to_response('upload.html', {'form': form}) -.. _binding uploaded files to a form: ../newforms/#binding-uploaded-files-to-a- form +.. _binding uploaded files to a form: ../forms/#binding-uploaded-files-to-a- form Notice that we have to pass ``request.FILES`` into the form's constructor; this is how file data gets bound into a form. @@ -79,10 +79,10 @@ methods to access the uploaded content: In practice, it's often easiest simply to use ``chunks()`` all the time; see the example below. - ``UploadedFile.file_name`` + ``UploadedFile.name`` The name of the uploaded file (e.g. ``my_file.txt``). - ``UploadedFile.file_size`` + ``UploadedFile.size`` The size, in bytes, of the uploaded file. There are a few other methods and attributes available on ``UploadedFile`` @@ -169,10 +169,10 @@ All ``UploadedFile`` objects define the following methods/attributes: Returns ``True`` if you can expect more than one chunk when calling ``UploadedFile.chunks(self, chunk_size)``. - ``UploadedFile.file_size`` + ``UploadedFile.size`` The size, in bytes, of the uploaded file. - ``UploadedFile.file_name`` + ``UploadedFile.name`` The name of the uploaded file as provided by the user. ``UploadedFile.content_type`` diff --git a/docs/url_dispatch.txt b/docs/url_dispatch.txt index 7d67cd5b53..266e4a5799 100644 --- a/docs/url_dispatch.txt +++ b/docs/url_dispatch.txt @@ -373,7 +373,7 @@ Including other URLconfs At any point, your ``urlpatterns`` can "include" other URLconf modules. This essentially "roots" a set of URLs below other ones. -For example, here's the URLconf for the `Django website`_ itself. It includes a +For example, here's the URLconf for the `Django Web site`_ itself. It includes a number of other URLconfs:: from django.conf.urls.defaults import * @@ -390,7 +390,7 @@ Django encounters ``include()``, it chops off whatever part of the URL matched up to that point and sends the remaining string to the included URLconf for further processing. -.. _`Django website`: http://www.djangoproject.com/ +.. _`Django Web site`: http://www.djangoproject.com/ Captured parameters ------------------- |