2
0
Эх сурвалжийг харах

Fixed #22796 -- Added a more basic explanations of forms.

Thanks bmispelon, kezabelle, jorgecarleitao, and timgraham for reviews.
Daniele Procida 10 жил өмнө
parent
commit
cd20b5d186

+ 4 - 0
docs/ref/forms/api.txt

@@ -376,6 +376,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
 ------------------------
 
@@ -492,6 +494,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
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

+ 2 - 0
docs/spelling_wordlist

@@ -1,3 +1,4 @@
 Aalto
 accessor
 Aceh
@@ -153,6 +154,7 @@ dirmod
 distro
 divisibleby
 django
+Django's
 djangojs
 djangonaut
 djangoproject

+ 1 - 1
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

+ 508 - 224
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's role in forms
+======================
+
+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.
+
+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.
+
+Django handles three distinct parts of the work involved in forms.
+
+* preparing and restructuring data ready for rendering
+* creating HTML forms for the data
+* receiving and processing submitted forms and data from the client
+
+It's *possible* to write code that does all of this manually, but Django can
+take care of it all for you.
+
+Forms in Django
+===============
+
+We've described HTML forms briefly, but an HTML ``<form>`` is just one part of
+the machinery required.
+
+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.
+
+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.
 
-``django.forms`` is Django's form-handling 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.
 
-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:
+When we instantiate a form, we can opt to leave it empty or pre-populate it, for
+example with:
 
-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.
+* 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
 
-Overview
-========
+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.
 
-The library deals with these concepts:
+Building a form
+===============
 
-.. glossary::
+The work that needs to done
+---------------------------
 
-    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.
+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:
 
-    Field
-        A class that is responsible for doing validation, e.g.
-        an ``EmailField`` that makes sure its data is a valid email address.
+.. code-block:: html+django
 
-    Form
-        A collection of fields that knows how to validate itself and
-        display itself as HTML.
+    <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>
+
+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.
+
+You'll need a view that renders the template containing the HTML form, and
+that can supply the ``current_name`` field as appropriate.
+
+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.
 
-    Form Assets (the ``Media`` class)
-        The CSS and JavaScript resources that are required to render a form.
+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.
 
-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).
+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.
 
-Form objects
-============
+At this point it's much easier to get Django to do most of this work for us.
 
-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.
+Building a form in Django
+-------------------------
 
-For example, consider a form used to implement "contact me" functionality on a
-personal Web site:
+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)
 
-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`.
+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).
 
-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.
+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.
+
+The whole form, when rendered for the first time, will look like:
+
+.. 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
-
-   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
-
-       return render(request, 'contact.html', {
-           'form': form,
-       })
-
-
-There are three possible code paths here:
-
-+------------------+---------------+-----------------------------------------+
-| 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.                          |
-+------------------+---------------+-----------------------------------------+
+    from django.shortcuts import render
+    from django.http import HttpResponseRedirect
+
+    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/')
+
+        # if a GET (or any other method) we'll create a blank form
+        else:
+            form = NameForm()
+
+        return render(request, 'name.html', {'form': form})
+
+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.
+
+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`.
 
-To see how to handle file uploads with your form, see
-:ref:`binding-uploaded-files`.
+Widgets
+^^^^^^^
 
-Processing the data from a form
--------------------------------
+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">``.
 
-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.
+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.
+
+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.
-
-Customizing the form template
------------------------------
-
-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::
-
-    <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>
-
-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::
+See :ref:`ref-forms-api-outputting-html` for more on this.
+
+Rendering fields manually
+-------------------------
+
+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:
+
+.. 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:
+
+.. 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>
@@ -297,32 +580,34 @@ over them::
 Non-field errors (and/or hidden field errors that are rendered at the top of
 the form when using helpers like ``form.as_p()``) will be rendered with an
 additional class of ``nonfield`` to help distinguish them from field-specific
-errors. For example, ``{{ form.non_field_errors }}`` would look like::
+errors. For example, ``{{ form.non_field_errors }}`` would look like:
+
+.. code-block:: html+django
 
     <ul class="errorlist nonfield">
         <li>Generic validation error</li>
     </ul>
 
+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::
-
-    <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>
+loop:
 
-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:
+.. code-block:: html+django
+
+    {% 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``.
@@ -341,7 +626,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
@@ -360,7 +645,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 #}
@@ -373,11 +660,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.
@@ -385,22 +672,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
@@ -412,15 +698,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 }}
@@ -430,12 +715,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>`.