diff --git a/docs/add_ons.txt b/docs/add_ons.txt index 029e314f12..3f226386fd 100644 --- a/docs/add_ons.txt +++ b/docs/add_ons.txt @@ -76,7 +76,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/api_stability.txt b/docs/api_stability.txt index 769359b75e..2a10f34a41 100644 --- a/docs/api_stability.txt +++ b/docs/api_stability.txt @@ -115,6 +115,6 @@ change: .. _template language: ../templates/ .. _transactions: ../transactions/ .. _url dispatch: ../url_dispatch/ -.. _forms and validation: ../forms/ +.. _forms and validation: ../oldforms/ .. _serialization: ../serialization/ .. _authentication: ../authentication/ diff --git a/docs/authentication.txt b/docs/authentication.txt index cd76731bc4..5e6b4b1a8b 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 diff --git a/docs/custom_model_fields.txt b/docs/custom_model_fields.txt index cbaac873e3..86d2986ffc 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 @@ -493,8 +493,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)`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 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..7781191e35 100644 --- a/docs/forms.txt +++ b/docs/forms.txt @@ -1,700 +1,2523 @@ -=============================== -Forms, fields, and manipulators -=============================== +================= +The forms library +================= -Forwards-compatibility note -=========================== +``django.forms`` is Django's fantastic new form-handling library. It's a +replacement for the old form/manipulator/validation framework, which has been +moved to ``django.oldforms``. This document explains how to use this new +library. -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`_. +Migration plan +============== -If you have legacy form/manipulator code, read the "Migration plan" section in -that document to understand how we're making the switch. +``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. -.. _newforms documentation: ../newforms/ +That has direct repercussions on the forward compatibility of your code. Please +read the following migration plan and code accordingly: -Introduction + * 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.oldforms`` ("manipulators") system before it, +``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. + +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 ============ -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. +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. -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:: +Start with this basic ``Form`` subclass, which we'll call ``ContactForm``:: - from django.db import models + from django import forms - PLACE_TYPES = ( - (1, 'Bar'), - (2, 'Restaurant'), - (3, 'Movie Theater'), - (4, 'Secret Hideout'), - ) + class ContactForm(forms.Form): + subject = forms.CharField(max_length=100) + message = forms.CharField() + sender = forms.EmailField() + cc_myself = forms.BooleanField(required=False) - 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) +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. - class Admin: - pass +Creating ``Form`` instances +--------------------------- - def __unicode__(self): - return self.name +A ``Form`` instance is either **bound** to a set of data, or **unbound**. -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? + * 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. -Automatic Manipulators -====================== + * 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. -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:: +To create an unbound ``Form`` instance, simply instantiate the class:: - >>> from mysite.myapp.models import Place - >>> Place.AddManipulator - <class 'django.models.manipulators.AddManipulator'> - >>> Place.ChangeManipulator - <class 'django.models.manipulators.ChangeManipulator'> + >>> f = ContactForm() -Using the ``AddManipulator`` +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 ---------------------------- -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:: +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:: - from django.shortcuts import render_to_response - from django.http import Http404, HttpResponse, HttpResponseRedirect - from django import forms - from mysite.myapp.models import Place + >>> data = {'subject': 'hello', + ... 'message': 'Hi there', + ... 'sender': 'foo@example.com', + ... 'cc_myself': True} + >>> f = ContactForm(data) + >>> f.is_valid() + True - def naive_create_place(request): - """A naive approach to creating places; don't actually use this!""" - # Create the AddManipulator. - manipulator = Place.AddManipulator() +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:: - # 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() + >>> data = {'subject': '', + ... 'message': 'Hi there', + ... 'sender': 'invalid e-mail address', + ... 'cc_myself': True} + >>> f = ContactForm(data) + >>> f.is_valid() + False - # Convert the request data (which will all be strings) into the - # appropriate Python types for those fields. - manipulator.do_html2python(new_data) +Access the ``errors`` attribute to get a dictionary of error messages:: - # Save the new object. - new_place = manipulator.save(new_data) + >>> f.errors + {'sender': [u'Enter a valid e-mail address.'], 'subject': [u'This field is required.']} - # It worked! - return HttpResponse("Place created: %s" % new_place) +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. -The ``naive_create_place`` example works, but as you probably can tell, this -view has a number of problems: +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``. - * 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. +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. - * Even if you *do* perform validation, there's still no way to give that - information to the user in any sort of useful way. +Behavior of unbound forms +~~~~~~~~~~~~~~~~~~~~~~~~~ - * You'll have to separately create a form (and view) that submits to this - page, which is a pain and is redundant. +It's meaningless to validate a form with no data, but, for the record, here's +what happens with unbound forms:: -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:: + >>> f = ContactForm() + >>> f.is_valid() + False + >>> f.errors + {} - 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}) +Accessing "clean" data +---------------------- -(This view, as well as all the following ones, has the same imports as in the -first example above.) +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. -The ``forms.FormWrapper`` object is a wrapper that templates can -easily deal with to create forms. Here's the ``naive_create_form.html`` -template:: +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. - {% extends "base.html" %} +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:: - {% block content %} - <h1>Create a place:</h1> + >>> 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'} - <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> +.. 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': + 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> - {% 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: +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. - * 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. +The following is equivalent but a bit more explicit:: - * 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. + <form method="post" action=""> + <table>{{ form.as_table }}</table> + <input type="submit" /> + </form> - * To avoid name conflicts, the ``id`` values of form elements take the - form "id_*fieldname*". +``form.as_ul`` and ``form.as_p`` are also available, as you may expect. -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:: +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 create_place_with_validation(request): - manipulator = Place.AddManipulator() - new_data = request.POST.copy() +Complex template output +~~~~~~~~~~~~~~~~~~~~~~~ - # 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) +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. -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):: +The easiest way is to iterate over the form's fields, with +``{% for field in form %}``. For example:: - {% extends "base.html" %} + <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> - {% block content %} +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. - <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 %} +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> - {% endblock %} +Highlighting required fields in templates +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Still, this has its own problems: +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:: - * 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> + <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> - {% endblock %} -The second two arguments to ``FormWrapper`` (``new_data`` and ``errors``) -deserve some mention. +The ``{% if field.field.required %}*{% endif %}`` fragment is the relevant +addition here. It adds the asterisk only if the field is required. -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. +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. -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. +Binding uploaded files to a form +-------------------------------- -Using the ``ChangeManipulator`` -------------------------------- +**New in Django development version** -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:: +Dealing with forms that have ``FileField`` and ``ImageField`` fields +is a little more complicated than a normal form. - 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 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:: +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/"> -Next, you'll need to treat the field in the template slightly differently. A -``FileField`` or ``ImageField`` is represented by *two* HTML form elements. +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:: -For example, given this field in a model:: + # 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) - photo = model.ImageField('/path/to/upload/location') +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):: -You'd need to display two formfields in the template:: + # Bound form with an image field, data from the request + >>> f = ContactFormWithMugshot(request.POST, request.FILES) - <p><label for="id_photo">Photo:</label> {{ form.photo }}{{ form.photo_file }}</p> +Constructing an unbound form is the same as always -- just omit both +form data *and* file data:: -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. + # Unbound form with a image field + >>> f = ContactFormWithMugshot() -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. +Testing for multipart forms +~~~~~~~~~~~~~~~~~~~~~~~~~~~ -For example, following the ``new_data`` convention, you might do something like -this:: +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:: - new_data = request.POST.copy() - new_data.update(request.FILES) + >>> f = ContactFormWithMugshot() + >>> f.is_multipart() + True -Validators -========== +Here's an example of how you might use this in a template:: -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:: + {% 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.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 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.core import validators from django import forms - class ContactManipulator(forms.Manipulator): - def __init__(self): - self.fields = ( - # ... snip fields as above ... - forms.EmailField(field_name="to", validator_list=[self.isValidToAddress]) - ) + 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 - 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.") +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:: -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``. + class ContactForm(forms.Form): + subject = forms.CharField(max_length=100) + message = forms.CharField() + senders = MultiEmailField() + cc_myself = forms.BooleanField(required=False) -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. +Widgets +======= -.. admonition:: Note:: +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. - At the point validators are called all data will still be - strings (as ``do_html2python`` hasn't been called yet). +Django provides a representation of all the basic HTML widgets, plus some +commonly used groups of widgets: -Also, because consistency in user interfaces is important, we strongly urge you -to put punctuation at the end of your validation messages. + ============================ =========================================== + 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. + ============================ =========================================== -When are validators called? +**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 --------------------------- -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. +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. -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. +For example, the static media definition for our Calendar Widget could +also be defined in a dynamic fashion:: -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:: + class CalendarWidget(forms.TextInput): + def _media(self): + return forms.Media(css={'all': ('pretty.css',)}, + js=('animations.js', 'actions.js')) + media = property(_media) - def my_custom_validator(field_data, all_data): - # ... - my_custom_validator.always_test = True +See the section on `Media objects`_ for more details on how to construct +return values for dynamic media properties. -This validator will always be executed for any field it is attached to. +Paths in media definitions +-------------------------- -Ready-made validators ---------------------- +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/``:: -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``. + class CalendarWidget(forms.TextInput): + class Media: + css = { + 'all': ('/css/pretty.css',), + } + js = ('animations.js', 'http://othersite.com/actions.js') -The following validators should all be self-explanatory. Each one provides a -check for the given property: + >>> 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> - * 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 +Media objects +------------- -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. +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. -For example:: +However, Media objects have some other interesting properties. - from django.core import validators - from django import forms +Media subsets +~~~~~~~~~~~~~ - power_validator = validators.IsAPowerOf(2) +If you only want media of a particular type, you can use the subscript operator +to filter out a medium of interest. For example:: - class InstallationManipulator(forms.Manipulator) - def __init__(self): - self.fields = ( - ... - forms.IntegerField(field_name = "size", validator_list=[power_validator]) - ) + >>> 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, ``validators.IsAPowerOf(...)`` returned something that could be used as -a validator (in this case, a check that a number was a power of 2). + >>> print w.media['css'] + <link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" /> -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. +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. -``AlwaysMatchesOtherField`` - Takes a field name and the current field is valid if and only if its value - matches the contents of the other field. +Combining media objects +~~~~~~~~~~~~~~~~~~~~~~~ -``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. +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:: -``RequiredIfOtherFieldGiven`` - Takes a field name of the current field is only required if the other - field has a value. + class CalendarWidget(forms.TextInput): + class Media: + css = { + 'all': ('pretty.css',) + } + js = ('animations.js', 'actions.js') -``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. + class OtherWidget(forms.TextInput): + class Media: + js = ('whizbang.js',) -``RequiredIfOtherFieldNotGiven`` - Takes the name of the other field and this field is only required if the - other field has no value. + >>> 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 +-------------- -``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. +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. - 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. +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:: - 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. + class ContactForm(forms.Form): + date = DateField(widget=CalendarWidget) + name = CharField(max_length=40, widget=OtherWidget) -``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. + >>> 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> -``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). +If you want to associate additional media with a form -- for example, CSS for form +layout -- simply add a media declaration to the form:: - 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). + class ContactForm(forms.Form): + date = DateField(widget=CalendarWidget) + name = CharField(max_length=40, widget=OtherWidget) -``IsAPowerOf`` - Takes an integer argument and when called as a validator, checks that the - field being validated is a power of the integer. + 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> -``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. +Formsets +======== -``MatchesRegularExpression`` - Takes a regular expression (a string) as a parameter and validates the - field value against it. +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:: -``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. + >>> from django import newforms as forms + >>> class ArticleForm(forms.Form): + ... title = forms.CharField() + ... pub_date = forms.DateField() -``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. +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:: -``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). + >>> from django.newforms.formsets import formset_factory + >>> ArticleFormSet = formset_factory(ArticleForm) -.. _`generic views`: ../generic_views/ -.. _`models API`: ../model-api/ -.. _settings: ../settings/ +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.forms``. 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/generic_views.txt b/docs/generic_views.txt index a7602524a9..86a04a700c 100644 --- a/docs/generic_views.txt +++ b/docs/generic_views.txt @@ -984,12 +984,12 @@ In addition to ``extra_context``, the template's context will be: <p>{{ form.address.label_tag }} {{ form.address }}</p> </form> - See the `newforms documentation`_ for more information about using + See the `forms documentation`_ for more information about using ``Form`` objects in templates. .. _authentication system: ../authentication/ .. _ModelForm docs: ../newforms/modelforms -.. _newforms documentation: ../newforms/ +.. _forms documentation: ../forms/ ``django.views.generic.create_update.update_object`` ---------------------------------------------------- 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/localflavor.txt b/docs/localflavor.txt index f30c6a542b..74224ab413 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): @@ -58,10 +58,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): @@ -92,7 +92,7 @@ them:: .. _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 ============== diff --git a/docs/model-api.txt b/docs/model-api.txt index 4975953b97..9a353c0ec4 100644 --- a/docs/model-api.txt +++ b/docs/model-api.txt @@ -716,7 +716,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 ------------------- diff --git a/docs/modelforms.txt b/docs/modelforms.txt index 9c06bc409d..91d3a9fac9 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 ------------------------------------ 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..7ee2cf3735 --- /dev/null +++ b/docs/oldforms.txt @@ -0,0 +1,700 @@ +=============================== +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 django.forms system, which we have begun to document in the +`forms documentation`_. + +If you have legacy form/manipulator code, read the "Migration plan" section in +that document to understand how we're making the switch. + +.. _forms documentation: ../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/upload_handling.txt b/docs/upload_handling.txt index e11d0d94f0..68323068f6 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.