diff options
| author | Daniele Procida <daniele@vurt.org> | 2014-06-08 19:30:52 +0100 |
|---|---|---|
| committer | Tim Graham <timograham@gmail.com> | 2014-06-24 14:58:53 -0400 |
| commit | 60375eda324d708870dc2ba18b4ac4d6114a68f6 (patch) | |
| tree | cb71d7390ce4b45bdf27ba5000100d095ce97b0d /docs | |
| parent | 088b30f49b0f5e14fc6876eb424af340d5479dac (diff) | |
[1.7.x] Fixed #22796 -- Added a more basic explanations of forms.
Thanks bmispelon, kezabelle, jorgecarleitao, and timgraham for reviews.
Backport of cd20b5d186 from master
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/ref/forms/api.txt | 4 | ||||
| -rw-r--r-- | docs/spelling_wordlist | 2 | ||||
| -rw-r--r-- | docs/topics/forms/formsets.txt | 2 | ||||
| -rw-r--r-- | docs/topics/forms/index.txt | 708 |
4 files changed, 502 insertions, 214 deletions
diff --git a/docs/ref/forms/api.txt b/docs/ref/forms/api.txt index 4fef753172..3b687ed73d 100644 --- a/docs/ref/forms/api.txt +++ b/docs/ref/forms/api.txt @@ -365,6 +365,8 @@ 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 :doc:`/ref/forms/validation`. +.. _ref-forms-api-outputting-html: + Outputting forms as HTML ------------------------ @@ -481,6 +483,8 @@ it calls its ``as_table()`` method behind the scenes:: <tr><th><label for="id_sender">Sender:</label></th><td><input type="email" 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> +.. _ref-forms-api-styling-form-rows: + Styling required or erroneous form rows ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/spelling_wordlist b/docs/spelling_wordlist index 7ba6399fb8..55dbb88684 100644 --- a/docs/spelling_wordlist +++ b/docs/spelling_wordlist @@ -1,3 +1,4 @@ +à Aalto accessor Aceh @@ -152,6 +153,7 @@ dirmod distro divisibleby django +Django's djangojs djangonaut djangoproject diff --git a/docs/topics/forms/formsets.txt b/docs/topics/forms/formsets.txt index b7f0317875..59e6f65593 100644 --- a/docs/topics/forms/formsets.txt +++ b/docs/topics/forms/formsets.txt @@ -56,7 +56,7 @@ 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 +number of forms it generates from the initial data. Let's take a look at an example:: >>> import datetime diff --git a/docs/topics/forms/index.txt b/docs/topics/forms/index.txt index 3ccafcb192..054cb19deb 100644 --- a/docs/topics/forms/index.txt +++ b/docs/topics/forms/index.txt @@ -2,128 +2,372 @@ Working with forms ================== +.. currentmodule:: django.forms + .. admonition:: About this document - This document provides an introduction to Django's form handling features. - For a more detailed look at specific areas of the forms API, see - :doc:`/ref/forms/api`, :doc:`/ref/forms/fields`, and + This document provides an introduction to the basics of web forms and how + they are handled in Django. For a more detailed look at specific areas of + the forms API, see :doc:`/ref/forms/api`, :doc:`/ref/forms/fields`, and :doc:`/ref/forms/validation`. -.. highlightlang:: html+django +Unless you're planning to build websites and applications that do nothing but +publish content, and don't accept input from your visitors, you're going to +need to understand and use forms. + +Django provides a range of tools and libraries to help you build forms to +accept input from site visitors, and process and respond to the input. + +HTML forms +========== + +In HTML, a form is a collection of elements inside ``<form>...</form>`` that +allow a visitor to do things like enter text, select options, manipulate +objects or controls, and so on, and then send that information back to the +server. + +Some of these form interface elements - text input or checkboxes - are fairly +simple and built-in to HTML itself. Others are much more complex; an interface +that pops up a date picker or allows you to move a slider or manipulate +controls will typically use JavaScript and CSS as well as HTML form ``<input>`` +elements to achieve these effects. + +As well as its ``<input>`` elements, a form must specify two things: + +* *where*: the URL to which the data corresponding to the user's input should + be returned + +* *how*: the HTTP method the data should be returned by + +As an example, the standard Django login form contains several ``<input>`` +elements: one of ``type="text"`` for the username, one of ``type="password"`` +for the password, and one of one of ``type="submit"`` for the "Log in" button. +It also contains some hidden text fields that the user doesn't see, that Django +uses to determine what to do next. + +It also tells the browser that the form data should be sent to the URL +specified in the ``<form>``’s ``action`` attribute - ``/admin/`` - and that it +should be sent using the HTTP mechanism specified by the ``method`` attribute - +``post``. + +When the ``<input type="submit" value="Log in">`` element is triggered, the +data are returned to ``/admin/``. + +``GET`` and ``POST`` +-------------------- + +``GET`` and ``POST`` are the only HTTP methods to use when dealing with forms. + +Django's login form is returned using the ``POST`` method, in which the browser +bundles up the form data, encodes them for transmission, sends them back to +the server, and then receives its response. + +``GET`` by contrast bundles the submitted data into a string, and uses this to +compose a URL. The URL contains the address where the data must be sent, as +well as the data keys and values. You can see this in action if you do a search +in the Django documentation, which will produce a URL of the form +``https://docs.djangoproject.com/search/?q=forms&release=1``. + +``GET`` and ``POST`` are typically used for different purposes. + +Any request that could be used to change the state of the system - for example, +a request that makes changes in the database - should use ``POST``. ``GET`` +should be used only for requests that do not affect the state of the system. + +``GET`` would also be unsuitable for a password form, because the password +would appear in the URL, and thus also in browser history and server logs, +all in plaintext. Neither would it be suitable for large quantities of data, +or for binary data, such as an image. A web application that uses ``GET`` +requests for admin forms is a security risk: it can be easy for an attacker to +mimic a form's request to gain access to sensitive parts of the system. +``POST``, coupled with other protections like Django's:doc:`CSRF protection +</ref/contrib/csrf/>` offers more control over access. + +On the other hand, ``GET`` is suitable for things like a web search form, +because the URLs that represent a ``GET`` request can easily be bookmarked, +shared, or resubmitted. -``django.forms`` is Django's form-handling library. +Django's role in forms +====================== -While it is possible to process form submissions just using Django's -:class:`~django.http.HttpRequest` class, using the form library takes care of a -number of common form-related tasks. Using it, you can: +Handling forms is a complex business. Consider Django's admin, where numerous +items of data of various different types may need to be prepared for display in +a form, rendered as HTML, edited using a convenient interface, returned to the +server, validated and cleaned up, and then saved or passed on for further +processing. -1. Display an HTML form with automatically generated form widgets. -2. Check submitted data against a set of validation rules. -3. Redisplay a form in the case of validation errors. -4. Convert submitted form data to the relevant Python data types. +Django's form functionality can simplify and automate vast portions of this +work, and also do it more safely and securely than most programmers would be +able to do in code they wrote themselves. -Overview -======== +Django handles three distinct parts of the work involved in forms. -The library deals with these concepts: +* preparing and restructuring data ready for rendering +* creating HTML forms for the data +* receiving and processing submitted forms and data from the client -.. glossary:: +It's *possible* to write code that does all of this manually, but Django can +take care of it all for you. - 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. +Forms in Django +=============== - Field - A class that is responsible for doing validation, e.g. - an ``EmailField`` that makes sure its data is a valid email address. +We've described HTML forms briefly, but an HTML ``<form>`` is just one part of +the machinery required. - Form - A collection of fields that knows how to validate itself and - display itself as HTML. +In the context of a web application, 'form' might refer to that HTML +``<form>``, or to the Django :class:`Form` that produces it, or to the +structured data returned when it is submitted, or to the end-to-end working +collection of these parts. - Form Assets (the ``Media`` class) - The CSS and JavaScript resources that are required to render a form. +The Django :class:`Form` class +------------------------------ + +At the heart of this system of components is Django's :class:`Form` class. In +much the same way that a Django model describes the logical structure of an +object, its behavior, and the way its parts are represented to us, a +:class:`Form` class describes a form and determines how it works and appears. + +In a similar way that a model class's fields map to database fields, a form +class's fields map to HTML form ``<input>`` elements. (A :class:`ModelForm` +maps a model class's fields to HTML form ``<input>`` elements via a +:class:`Form`; this is what the Django admin is based upon.) + +A form's fields are themselves classes; they manage form data and perform +validation when a form is submitted. A ``DateField`` and a ``FileField`` handle +very different kinds of data and have to do different things with them. + +A form field is represented to a user in the browser as a HTML "widget" - a +piece of user interface machinery. Each field type has an appropriate default +:doc:`Widget class </ref/forms/widgets/>`, but these can be overridden as +required. + +Instantiating, processing, and rendering forms +---------------------------------------------- + +When rendering an object in Django we generally: + +1. get hold of it in the view (fetch it from the database, for example) +2. pass it to the template context +3. expand it to HTML markup using template variables + +Rendering a form in a template involves nearly the same work as rendering any +other kind of object, but there are some key differences. + +In the case of a model instance that contained no data it would rarely if ever +be useful to do anything with one in a template. On the other hand, it makes +perfect sense to render an unpopulated form - that's what we do when we want +the user to populate it. -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). +So when we handle a model instance in a view we typically retrieve it from the +database; when we're dealing with a form we typically instantiate it in the +view. + +When we instantiate a form, we can opt to leave it empty or pre-populate it, for +example with: + +* data from a saved model instance (as in the case of admin forms for editing) +* data that we have collated from other sources +* data received from a previous HTML form submission + +The last of these cases is the most interesting, because it's what makes it +possible for users not just to read a website, but to send information back to +it too. + +Building a form +=============== + +The work that needs to done +--------------------------- + +Suppose you want to create a simple form on your website, to obtain the user's +name. You'd need something like this in your template: + +.. code-block:: html+django + + <form action="/your-name/" method="post"> + <label for="your_name">Your name: </label> + <input id="your_name" type="text" name="your_name" value="{{ current_name }}"> + <input type="submit" value="OK"> + </form> -Form objects -============ +This tells the browser to return the form data to the URL ``/your-name/``, using +the ``POST`` method. It will display a text field, labeled "Your name:", and a +button marked "OK". If the template context contains a ``current_name`` +variable, that will be used to pre-fill the ``your_name`` field. -A Form object encapsulates a sequence of form fields and a collection of -validation rules that must be fulfilled in order for the form to be accepted. -Form classes are created as subclasses of ``django.forms.Form`` and -make use of a declarative style that you'll be familiar with if you've used -Django's database models. +You'll need a view that renders the template containing the HTML form, and +that can supply the ``current_name`` field as appropriate. -For example, consider a form used to implement "contact me" functionality on a -personal Web site: +When the form is submitted, the ``POST`` request sent to the server will contain +the form data. + +Now you'll also need a view corresponding to that ``/your-name/`` URL which will +find the appropriate key/value pairs in the request and process them. + +This is a very simple form. In practice, a form might contain dozens or +hundreds of fields, many of which might need to be pre-populated, and we might +expect the user to work through the edit-submit cycle several times before +concluding the operation. + +We might require some validation to occur in the browser, even before the form +is submitted; we might want to use much more complex fields, that allow the +user to do things like pick dates from a calendar; and so on. + +At this point it's much easier to get Django to do most of this work for us. + +Building a form in Django +------------------------- + +The :class:`Form` class +^^^^^^^^^^^^^^^^^^^^^^^ + +We already know what we want our HTML form to look like. Our starting point for +it in Django is this: .. code-block:: python from django import forms - class ContactForm(forms.Form): - subject = forms.CharField(max_length=100) - message = forms.CharField() - sender = forms.EmailField() - cc_myself = forms.BooleanField(required=False) + class NameForm(forms.Form): + your_name = forms.CharField(label='Your name', max_length=100) + +This defines a :class:`Form` class with a single field (``your_name``). We've +applied a human-friendly label to the field, that will appear in the +``<label>`` when it's rendered (although in this case, the :attr:`~Field.label` +we specified is actually the same one that would be generated automatically if +we had omitted it). + +The field's maximum allowable length is defined by +:attr:`~CharField.max_length`. This does two things. It puts a +``maxlength="100"`` on the HTML ``<input>`` (so the browser should prevent the +user entering more than that many characters in the first place). It also means +that when Django receives the form back from the browser, it will validate the +length of the data. + +A :class:`Form` instance has an :meth:`~Form.is_valid()` method, which runs +validation routines for all its fields. When this method is called, if all +fields contain valid data, it will: + +* return ``True`` +* place the form's data in its :attr:`~Form.cleaned_data` attribute. -A form is composed of ``Field`` objects. In this case, our form has four -fields: ``subject``, ``message``, ``sender`` and ``cc_myself``. ``CharField``, -``EmailField`` and ``BooleanField`` are just three of the available field types; -a full list can be found in :doc:`/ref/forms/fields`. +The whole form, when rendered for the first time, will look like: -If your form is going to be used to directly add or edit a Django model, you can -use a :doc:`ModelForm </topics/forms/modelforms>` to avoid duplicating your model -description. +.. code-block:: html+django + + <label for="your_name">Your name: </label> + <input id="your_name" type="text" name="your_name" maxlength="100"> + +Note that it **does not** include the ``<form>`` tags, or a submit button. +We'll have to provide those ourselves in the template. .. _using-a-form-in-a-view: -Using a form in a view ----------------------- +The view +^^^^^^^^ + +Form data sent back to a Django website are processed by a view, generally the +same view that published the form. This allows us to reuse some of the same +logic. -The standard pattern for processing a form in a view looks like this: +To handle the form we need to instantiate it in the view for the URL where we +want it to be published: .. code-block:: python - from django.shortcuts import render - from django.http import HttpResponseRedirect + from django.shortcuts import render + from django.http import HttpResponseRedirect - def contact(request): - if request.method == 'POST': # If the form has been submitted... - # ContactForm was defined in the previous section - form = ContactForm(request.POST) # A form bound to the POST data - if form.is_valid(): # All validation rules pass - # Process the data in form.cleaned_data - # ... - return HttpResponseRedirect('/thanks/') # Redirect after POST - else: - form = ContactForm() # An unbound form + def get_name(request): + # if this is a POST request we need to process the form data + if request.method == 'POST': + # create a form instance and populate it with data from the request: + form = NameForm(request.POST) + # check whether it's valid: + if form.is_valid(): + # process the data in form.cleaned_data as required + # ... + # redirect to a new URL: + return HttpResponseRedirect('/thanks/') - return render(request, 'contact.html', { - 'form': form, - }) + # if a GET (or any other method) we'll create a blank form + else: + form = NameForm() + return render(request, 'name.html', {'form': form}) -There are three possible code paths here: +If we arrive at this view with a ``GET`` request, it will create an empty form +instance and place it in the template context to be rendered. This is what we +can expect to happen the first time we visit the URL. + +If the form is submitted using a ``POST`` request, the view will once again +create a form instance and populate it with data from the request: ``form = +NameForm(request.POST)`` (this is called "binding data to the form" - it is now +a *bound* form). + +We call the form's ``is_valid()`` method; if it's not ``True``, we go back to +the template with the form. This time the form is no longer empty (*unbound*) +so the HTML form will be populated with the data previously submitted, where it +can be edited and corrected as required. + +If ``is_valid()`` is ``True``, we'll now be able to find all the validated form +data in its ``cleaned_data`` attribute. We can use these data to update the +database or do other processing before sending an HTTP redirect to the browser +telling it where to go next. + +.. _topics-forms-index-basic-form-template: + +The template +^^^^^^^^^^^^ + +We don't need to do much in our ``name.html`` template. The simplest example +is: + +.. code-block:: html+django + + <form action="/your-name/" method="post"> + {% csrf_token %} + {{ form }} + <input type="submit" value="Submit" /> + </form> + +All the form's fields and their attributes will be unpacked into HTML markup +from that ``{{ form }}`` by Django's template language. + +.. admonition:: Forms and Cross Site Request Forgery protection + + Django ships with an easy-to-use :doc:`protection against Cross Site Request + Forgeries </ref/contrib/csrf>`. When submitting a form via ``POST`` with + CSRF protection enabled you must use the :ttag:`csrf_token` template tag + as in the preceding example. However, since CSRF protection is not + directly tied to forms in templates, this tag is omitted from the + following examples in this document. -+------------------+---------------+-----------------------------------------+ -| Form submitted? | Data? | What occurs | -+==================+===============+=========================================+ -| Not submitted | None yet | Template gets passed unbound instance | -| | | of ContactForm. | -+------------------+---------------+-----------------------------------------+ -| Submitted | Invalid data | Template gets passed bound instance | -| | | of ContactForm. | -+------------------+---------------+-----------------------------------------+ -| Submitted | Valid data | Valid data is processed. Redirect to a | -| | | "thanks" page. | -+------------------+---------------+-----------------------------------------+ +We now have a working web form, described by a Django :class:`Form`, processed +by a view, and rendered as an HTML ``<form>``. + +That's all you need to get started, but the forms framework puts a lot more at +your fingertips. Once you understand the basics of the process described above, +you should be aware of what else is readily available in the forms system +and know a little bit about some of the underlying machinery. + +More about Django :class:`Form` classes +======================================= + +All form classes are created as subclasses of ``django.forms.Form``, including +the :doc:`ModelForm </topics/forms/modelforms>` you encounter in Django's +admin. + +.. admonition:: Models and Forms + + In fact if your form is going to be used to directly add or edit a Django + model, a :doc:`ModelForm </topics/forms/modelforms>` can save you a great + deal of time, effort, and code, because it will build a form, along with the + appropriate fields and their attributes, from a ``Model`` class. + +Bound and unbound form instances +-------------------------------- The distinction between :ref:`ref-forms-api-bound-unbound` is important: @@ -134,37 +378,67 @@ The distinction between :ref:`ref-forms-api-bound-unbound` is important: is valid. If an invalid bound form is rendered, it can include inline error messages telling the user what data to correct. -Handling file uploads with a form ---------------------------------- +The form's ``is_bound()`` method will tell you whether a form has data bound to +it or not. + +More on fields +-------------- + +Consider a rather more useful form than our minimal example above, that +we could use to implement "contact me" functionality on a personal Web site: + +.. code-block:: python + + from django import forms + + class ContactForm(forms.Form): + subject = forms.CharField(max_length=100) + message = forms.CharField(widget=forms.Textarea) + sender = forms.EmailField() + cc_myself = forms.BooleanField(required=False) + +Our earlier form used a single field, ``your_name``, a ``CharField``. In this +case, our form has four fields: ``subject``, ``message``, ``sender`` and +``cc_myself``. :class:`CharField`, :class:`EmailField` and +:class:`BooleanField` are just three of the available field types; a full list +can be found in :doc:`/ref/forms/fields`. + +Widgets +^^^^^^^ -To see how to handle file uploads with your form, see -:ref:`binding-uploaded-files`. +Each form field has a corresponding :doc:`Widget class </ref/forms/widgets/>`, +which in turn corresponds to an HTML form widget such as ``<input +type="text">``. -Processing the data from a form -------------------------------- +In most cases, the field will have a sensible default widget. For example, by +default, a ``CharField`` will have a :class:`TextInput` widget, that produces an +``<input type="text">`` in the HTML. If you needed ``<input type="textarea">`` +instead, you'd specify the appropriate widget when defining your form field, +as we have done for the ``message`` field. -Once ``is_valid()`` returns ``True``, the successfully validated form data -will be in the ``form.cleaned_data`` dictionary. This data will have been -converted nicely into Python types for you. +Field data +^^^^^^^^^^ + +Whatever the data submitted with a form, once it has been successfully +validated by calling ``is_valid()`` (and ``is_valid()`` has returned ``True``), +the validated form data will be in the ``form.cleaned_data`` dictionary. This +data will have been converted nicely into Python types for you. .. note:: You can still access the unvalidated data directly from ``request.POST`` at this point, but the validated data is better. -In the above example, ``cc_myself`` will be a boolean value. Likewise, fields -such as ``IntegerField`` and ``FloatField`` convert values to a Python int and -float respectively. - -Read-only fields are not available in ``form.cleaned_data`` (and setting -a value in a custom ``clean()`` method won't have any effect). These -fields are displayed as text rather than as input elements, and thus are not -posted back to the server. +In the contact form example above, ``cc_myself`` will be a boolean value. +Likewise, fields such as :class:`IntegerField` and :class:`FloatField` convert +values to a Python ``int`` and ``float`` respectively. -Extending the earlier example, here's how the form data could be processed: +Here's how the form data could be processed in the view that handles this form: .. code-block:: python + from django.core.mail import send_mail + if form.is_valid(): subject = form.cleaned_data['subject'] message = form.cleaned_data['message'] @@ -175,50 +449,51 @@ Extending the earlier example, here's how the form data could be processed: if cc_myself: recipients.append(sender) - from django.core.mail import send_mail send_mail(subject, message, sender, recipients) - return HttpResponseRedirect('/thanks/') # Redirect after POST + return HttpResponseRedirect('/thanks/') .. tip:: For more on sending email from Django, see :doc:`/topics/email`. -Displaying a form using a template ----------------------------------- +Some field types need some extra handling. For example, files that are uploaded +using a form need to be handled differently (they can be retrieved from +``request.FILES``, rather than ``request.POST``). For details of how to handle +file uploads with your form, see :ref:`binding-uploaded-files`. -Forms are designed to work with the Django template language. In the above -example, we passed our ``ContactForm`` instance to the template using the -context variable ``form``. Here's a simple example template:: +Working with form templates +=========================== - <form action="/contact/" method="post">{% csrf_token %} - {{ form.as_p }} - <input type="submit" value="Submit" /> - </form> +All you need to do to get your form into a template is to place the form +instance into the template context. So if your form is called ``form`` in the +context, ``{{ form }}`` will render its ``<label>`` and ``<input>`` elements +appropriately. -The form only outputs its own fields; it is up to you to provide the surrounding -``<form>`` tags and the submit button. +Form rendering options +---------------------- -If your form includes uploaded files, be sure to include -``enctype="multipart/form-data"`` in the ``form`` element. If you wish to write -a generic template that will work whether or not the form has files, you can -use the :meth:`~django.forms.Form.is_multipart` attribute on the form:: +.. admonition:: Additional form template furniture - <form action="/contact/" method="post" - {% if form.is_multipart %}enctype="multipart/form-data"{% endif %}> + Don't forget that a form's output does *not* include the surrounding + ``<form>`` tags, or the form's ``submit`` control. You will have to provide + these yourself. -.. admonition:: Forms and Cross Site Request Forgery protection +There are other output options though for the ``<label>``/``<input>`` pairs: - Django ships with an easy-to-use :doc:`protection against Cross Site Request - Forgeries </ref/contrib/csrf>`. When submitting a form via POST with - CSRF protection enabled you must use the :ttag:`csrf_token` template tag - as in the preceding example. However, since CSRF protection is not - directly tied to forms in templates, this tag is omitted from the - following examples in this document. +* ``{{ form.as_table }}`` will render them as table cells wrapped in ``<tr>`` + tags + +* ``{{ form.as_p }}`` will render them wrapped in ``<p>`` tags + +* ``{{ form.as_ul }}`` will render them wrapped in ``<li>`` tags + +Note that you'll have to provide the surrounding ``<table>`` or ``<ul>`` +elements yourself. -``form.as_p`` will output the form with each form field and accompanying label -wrapped in a paragraph. Here's the output for our example template:: +Here's the output of ``{{ form.as_p }}`` for our ``ContactForm`` instance: + +.. code-block:: html+django - <form action="/contact/" method="post"> <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> @@ -227,8 +502,6 @@ wrapped in a paragraph. Here's the output for our example template:: <input type="email" 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> - <input type="submit" value="Submit" /> - </form> Note that each form field has an ID attribute set to ``id_<field-name>``, which is referenced by the accompanying label tag. This is important for ensuring @@ -236,45 +509,53 @@ forms are accessible to assistive technology such as screen reader software. You can also :ref:`customize the way in which labels and ids are generated <ref-forms-api-configuring-label>`. -You can also use ``form.as_table`` to output table rows (you'll need to provide -your own ``<table>`` tags) and ``form.as_ul`` to output list items. +See :ref:`ref-forms-api-outputting-html` for more on this. -Customizing the form template ------------------------------ +Rendering fields manually +------------------------- -If the default generated HTML is not to your taste, you can completely customize -the way a form is presented using the Django template language. Extending the -above example:: +We don't have to let Django unpack the form's fields; we can do it manually if +we like (allowing us to reorder the fields, for example). Each field is +available as an attribute of the form using ``{{ form.name_of_field }}``, and +in a Django template, will be rendered appropriately. For example: - <form action="/contact/" method="post"> - {{ form.non_field_errors }} - <div class="fieldWrapper"> - {{ form.subject.errors }} - <label for="id_subject">Email subject:</label> - {{ form.subject }} - </div> - <div class="fieldWrapper"> - {{ form.message.errors }} - <label for="id_message">Your message:</label> - {{ form.message }} - </div> - <div class="fieldWrapper"> - {{ form.sender.errors }} - <label for="id_sender">Your email address:</label> - {{ form.sender }} - </div> - <div class="fieldWrapper"> - {{ form.cc_myself.errors }} - <label for="id_cc_myself">CC yourself?</label> - {{ form.cc_myself }} - </div> - <p><input type="submit" value="Send message" /></p> - </form> +.. code-block:: html+django + + {{ form.non_field_errors }} + <div class="fieldWrapper"> + {{ form.subject.errors }} + <label for="id_subject">Email subject:</label> + {{ form.subject }} + </div> + <div class="fieldWrapper"> + {{ form.message.errors }} + <label for="id_message">Your message:</label> + {{ form.message }} + </div> + <div class="fieldWrapper"> + {{ form.sender.errors }} + <label for="id_sender">Your email address:</label> + {{ form.sender }} + </div> + <div class="fieldWrapper"> + {{ form.cc_myself.errors }} + <label for="id_cc_myself">CC yourself?</label> + {{ form.cc_myself }} + </div> + +Rendering form error messages +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The price of this flexibility of course is more work. Until now we haven't had +to worry about how to display form errors, because that's taken care of for us. +In this example we have had to make sure we take care of any errors for each +field and any errors for the form as a whole. Note ``{{ form.non_field_errors +}}`` at the top of the form and the template lookup for errors on each field. + +Using ``{{ form.name_of_field.errors }}`` displays a list of form errors, +rendered as an unordered list. This might look like: -Each named form-field can be output to the template using -``{{ form.name_of_field }}``, which will produce the HTML needed to display the -form widget. Using ``{{ form.name_of_field.errors }}`` displays a list of form -errors, rendered as an unordered list. This might look like:: +.. code-block:: html+django <ul class="errorlist"> <li>Sender is required.</li> @@ -282,7 +563,9 @@ errors, rendered as an unordered list. This might look like:: The list has a CSS class of ``errorlist`` to allow you to style its appearance. If you wish to further customize the display of errors you can do so by looping -over them:: +over them: + +.. code-block:: html+django {% if form.subject.errors %} <ol> @@ -292,26 +575,26 @@ over them:: </ol> {% endif %} +See :doc:`/ref/forms/api` for more on errors, styling, and working with form +attributes in templates. + Looping over the form's fields ------------------------------ If you're using the same HTML for each of your form fields, you can reduce duplicate code by looping through each field in turn using a ``{% for %}`` -loop:: +loop: - <form action="/contact/" method="post"> - {% for field in form %} - <div class="fieldWrapper"> - {{ field.errors }} - {{ field.label_tag }} {{ field }} - </div> - {% endfor %} - <p><input type="submit" value="Send message" /></p> - </form> +.. code-block:: html+django -Within this loop, ``{{ field }}`` is an instance of -:class:`~django.forms.BoundField`. ``BoundField`` also has the following -attributes, which can be useful in your templates: + {% for field in form %} + <div class="fieldWrapper"> + {{ field.errors }} + {{ field.label_tag }} {{ field }} + </div> + {% endfor %} + +Useful attributes on ``{{ field }}`` include: ``{{ field.label }}`` The label of the field, e.g. ``Email address``. @@ -333,7 +616,7 @@ attributes, which can be useful in your templates: some inline JavaScript and want to avoid hardcoding the field's ID. ``{{ field.value }}`` - The value of the field. e.g ``someone@example.com`` + The value of the field. e.g ``someone@example.com``. ``{{ field.html_name }}`` The name of the field that will be used in the input element's name @@ -352,7 +635,9 @@ attributes, which can be useful in your templates: ``{{ field.is_hidden }}`` This attribute is ``True`` if the form field is a hidden field and ``False`` otherwise. It's not particularly useful as a template - variable, but could be useful in conditional tests such as:: + variable, but could be useful in conditional tests such as: + +.. code-block:: html+django {% if field.is_hidden %} {# Do something special #} @@ -365,11 +650,11 @@ attributes, which can be useful in your templates: ``{{ char_field.field.max_length }}``. Looping over hidden and visible fields -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If you're manually laying out a form in a template, as opposed to relying on Django's default form layout, you might want to treat ``<input type="hidden">`` -fields differently than non-hidden fields. For example, because hidden fields +fields differently from non-hidden fields. For example, because hidden fields don't display anything, putting error messages "next to" the field could cause confusion for your users -- so errors for those fields should be handled differently. @@ -377,22 +662,21 @@ differently. Django provides two methods on a form that allow you to loop over the hidden and visible fields independently: ``hidden_fields()`` and ``visible_fields()``. Here's a modification of an earlier example that uses -these two methods:: +these two methods: - <form action="/contact/" method="post"> - {# Include the hidden fields #} - {% for hidden in form.hidden_fields %} - {{ hidden }} - {% endfor %} - {# Include the visible fields #} - {% for field in form.visible_fields %} - <div class="fieldWrapper"> - {{ field.errors }} - {{ field.label_tag }} {{ field }} - </div> - {% endfor %} - <p><input type="submit" value="Send message" /></p> - </form> +.. code-block:: html+django + + {# Include the hidden fields #} + {% for hidden in form.hidden_fields %} + {{ hidden }} + {% endfor %} + {# Include the visible fields #} + {% for field in form.visible_fields %} + <div class="fieldWrapper"> + {{ field.errors }} + {{ field.label_tag }} {{ field }} + </div> + {% endfor %} This example does not handle any errors in the hidden fields. Usually, an error in a hidden field is a sign of form tampering, since normal form @@ -404,15 +688,14 @@ Reusable form templates If your site uses the same rendering logic for forms in multiple places, you can reduce duplication by saving the form's loop in a standalone template and -using the :ttag:`include` tag to reuse it in other templates:: +using the :ttag:`include` tag to reuse it in other templates: - <form action="/contact/" method="post"> - {% include "form_snippet.html" %} - <p><input type="submit" value="Send message" /></p> - </form> +.. code-block:: html+django - # In form_snippet.html: + # In your form template: + {% include "form_snippet.html" %} + # In form_snippet.html: {% for field in form %} <div class="fieldWrapper"> {{ field.errors }} @@ -422,12 +705,11 @@ using the :ttag:`include` tag to reuse it in other templates:: If the form object passed to a template has a different name within the context, you can alias it using the ``with`` argument of the :ttag:`include` -tag:: +tag: - <form action="/comments/add/" method="post"> - {% include "form_snippet.html" with form=comment_form %} - <p><input type="submit" value="Submit comment" /></p> - </form> +.. code-block:: html+django + + {% include "form_snippet.html" with form=comment_form %} If you find yourself doing this often, you might consider creating a custom :ref:`inclusion tag<howto-custom-template-tags-inclusion-tags>`. |
