Copy-edited docs from [303] and [304]
git-svn-id: http://code.djangoproject.com/svn/django/trunk@306 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
1c947e50c9
commit
f6c4395329
223
docs/forms.txt
223
docs/forms.txt
|
@ -9,13 +9,13 @@ code. It is, and this document explains how the framework works.
|
||||||
.. admonition:: A note to the lazy
|
.. admonition:: A note to the lazy
|
||||||
|
|
||||||
If all you want to do is present forms for a user to create and/or
|
If all you want to do is present forms for a user to create and/or
|
||||||
update a given object, don't read any further but instead click thyself
|
update a given object, don't read any further. Instead, click thyself
|
||||||
over to the `generic views`_ documentation. The following exercises are
|
to the `generic views`_ documentation. The following exercises are
|
||||||
for those interested in how Django's form framework works and those
|
for those interested in how Django's form framework works and those
|
||||||
needing to do more than simple create/update.
|
needing to do more than simple creation/updating.
|
||||||
|
|
||||||
We'll take a top-down approach to examining Django's form validation framework
|
We'll take a top-down approach to examining Django's form validation framework,
|
||||||
since much of the time you won't need to use the lower-level APIs. Throughout
|
becuase 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::
|
this document, we'll be working with the following model, a "place" object::
|
||||||
|
|
||||||
PLACE_TYPES = (
|
PLACE_TYPES = (
|
||||||
|
@ -27,12 +27,12 @@ this document, we'll be working with the following model, a "place" object::
|
||||||
|
|
||||||
class Place(meta.Model):
|
class Place(meta.Model):
|
||||||
fields = (
|
fields = (
|
||||||
meta.CharField('name', 'name', maxlength=100),
|
meta.CharField('name', maxlength=100),
|
||||||
meta.CharField('address', 'address', maxlength=100, blank=True),
|
meta.CharField('address', maxlength=100, blank=True),
|
||||||
meta.CharField('city', 'city', maxlength=50, blank=True),
|
meta.CharField('city', maxlength=50, blank=True),
|
||||||
meta.USStateField('state', 'state'),
|
meta.USStateField('state'),
|
||||||
meta.CharField('zip_code', 'zip code', maxlength=5, blank=True),
|
meta.CharField('zip_code', maxlength=5, blank=True),
|
||||||
meta.IntegerField('place_type', 'place type', choices=PLACE_TYPES)
|
meta.IntegerField('place_type', choices=PLACE_TYPES)
|
||||||
)
|
)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
@ -74,55 +74,57 @@ POSTed data from the browser and creates a new ``Place`` object::
|
||||||
|
|
||||||
def naive_create_place(request):
|
def naive_create_place(request):
|
||||||
"""A naive approach to creating places; don't actually use this!"""
|
"""A naive approach to creating places; don't actually use this!"""
|
||||||
# Create the AddManipulator
|
# Create the AddManipulator.
|
||||||
manipulator = places.AddManipulator()
|
manipulator = places.AddManipulator()
|
||||||
|
|
||||||
# Make a copy of the POSTed data so that do_html2python can
|
# Make a copy of the POSTed data so that do_html2python can
|
||||||
# modify it in place (request.POST is immutable)
|
# modify it in place (request.POST is immutable).
|
||||||
new_data = request.POST.copy()
|
new_data = request.POST.copy()
|
||||||
|
|
||||||
# Convert the request data (which will all be strings) into the
|
# Convert the request data (which will all be strings) into the
|
||||||
# appropriate Python types for those fields
|
# appropriate Python types for those fields.
|
||||||
manipulator.do_html2python(new_data)
|
manipulator.do_html2python(new_data)
|
||||||
|
|
||||||
# Save the new object
|
# Save the new object.
|
||||||
new_place = manipulator.save(new_data)
|
new_place = manipulator.save(new_data)
|
||||||
|
|
||||||
# It worked!
|
# It worked!
|
||||||
return HttpResponse("Place created: %s" % new_place)
|
return HttpResponse("Place created: %s" % new_place)
|
||||||
|
|
||||||
The ``naive_create_place`` example works (somewhat), but as you probably can
|
The ``naive_create_place`` example works, but as you probably can tell, this
|
||||||
tell, there's all sorts of problems (some more subtle than others) with this view:
|
view has a number of problems:
|
||||||
|
|
||||||
* No validation of any sort is performed; if, for example, the ``name`` field
|
* 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
|
isn't given in ``request.POST``, the save step will cause a database error
|
||||||
because that field is required. Ugly.
|
because that field is required. Ugly.
|
||||||
|
|
||||||
* Even if you *do* perform validation, there's still no way to give that information
|
* Even if you *do* perform validation, there's still no way to give that
|
||||||
to the user is any sort of useful way.
|
information to the user is any sort of useful way.
|
||||||
|
|
||||||
* You'll have to separate create a form (and view) that submits to this page, which is
|
* You'll have to separate create a form (and view) that submits to this
|
||||||
a pain and is redundant.
|
page, which is a pain and is redundant.
|
||||||
|
|
||||||
Let's dodge these problems momentarily to take a look at how you could create a
|
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::
|
view with a form that submits to this flawed creation view::
|
||||||
|
|
||||||
def naive_create_place_form(request):
|
def naive_create_place_form(request):
|
||||||
"""Simplistic place form view; don't actually use anything like this!"""
|
"""Simplistic place form view; don't actually use anything like this!"""
|
||||||
# Create a FormWrapper object which the template can use; more
|
# Create a FormWrapper object that the template can use. Ignore
|
||||||
# on what the second two arguments to FormWrapper do later.
|
# the last two arguments to FormWrapper for now.
|
||||||
form = formfields.FormWrapper(places.AddManipulator(), {}, {})
|
form = formfields.FormWrapper(places.AddManipulator(), {}, {})
|
||||||
|
|
||||||
# Create a template, context, and response
|
# Create a template, context and response.
|
||||||
t = template_loader.get_template('places/naive_create_form')
|
t = template_loader.get_template('places/naive_create_form')
|
||||||
c = Context(request, {'form' : form})
|
c = Context(request, {
|
||||||
|
'form': form
|
||||||
|
})
|
||||||
return HttpResponse(t.render(c))
|
return HttpResponse(t.render(c))
|
||||||
|
|
||||||
(This view, as well as all the following ones, have the same imports as the
|
(This view, as well as all the following ones, has the same imports as in the
|
||||||
first example above does.)
|
first example above.)
|
||||||
|
|
||||||
The ``formfields.FormWrapper`` object is a wrapper that templates can
|
The ``formfields.FormWrapper`` object is a wrapper that templates can
|
||||||
easily deal with to create forms; here's the ``naive_create_form`` template::
|
easily deal with to create forms. Here's the ``naive_create_form`` template::
|
||||||
|
|
||||||
{% extends "base" %}
|
{% extends "base" %}
|
||||||
|
|
||||||
|
@ -147,18 +149,18 @@ some salient points of the above template::
|
||||||
creates the "right" type of widget for the form, as you can see with the
|
creates the "right" type of widget for the form, as you can see with the
|
||||||
``place_type`` field above.
|
``place_type`` field above.
|
||||||
|
|
||||||
* There isn't a way just to spit out the form; you'll still need to define
|
* 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 needs to be
|
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 mould.
|
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
|
If you must use tables, use tables. If you're a semantic purist, you can
|
||||||
probably find better HTML than the above template.
|
probably find better HTML than in the above template.
|
||||||
|
|
||||||
* To avoid name conflicts, the ``id``s of form elements take the form
|
* To avoid name conflicts, the ``id``s of form elements take the form
|
||||||
"id_*fieldname*".
|
"id_*fieldname*".
|
||||||
|
|
||||||
By creating a creation form we've solved problem number 3 above, but we still don't
|
By creating a creation form we've solved problem number 3 above, but we still
|
||||||
have any validation; if you enter bad data into any of the . Let's revise the validation
|
don't have any validation. Let's revise the validation issue by writing a new
|
||||||
issue by writing a new creation view that takes into account validation::
|
creation view that takes validation into account::
|
||||||
|
|
||||||
def create_place_with_validation(request):
|
def create_place_with_validation(request):
|
||||||
manipulator = places.AddManipulator()
|
manipulator = places.AddManipulator()
|
||||||
|
@ -168,7 +170,9 @@ issue by writing a new creation view that takes into account validation::
|
||||||
errors = manipulator.get_validation_errors(new_data)
|
errors = manipulator.get_validation_errors(new_data)
|
||||||
if errors:
|
if errors:
|
||||||
t = template_loader.get_template('places/errors')
|
t = template_loader.get_template('places/errors')
|
||||||
c = Context(request, {'errors' : errors}
|
c = Context(request, {
|
||||||
|
'errors': errors
|
||||||
|
}
|
||||||
return HttpResponse(t.render(c))
|
return HttpResponse(t.render(c))
|
||||||
else:
|
else:
|
||||||
manipulator.do_html2python(request.POST)
|
manipulator.do_html2python(request.POST)
|
||||||
|
@ -192,25 +196,26 @@ on an error page (templated, of course)::
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
Still, this now has its own problems:
|
Still, this has its own problems:
|
||||||
|
|
||||||
* There's still the issue of creating a seperate (redundant) view for the
|
* There's still the issue of creating a separate (redundant) view for the
|
||||||
submission form.
|
submission form.
|
||||||
|
|
||||||
* Errors, though nicely presented are on a seperate page, so the user will have
|
* Errors, though nicely presented, are on a separate page, so the user will
|
||||||
to use the "back" button to fix errors -- not exactly usable!
|
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
|
The best way to deal with these issues is to collapse the two views -- the form
|
||||||
submission -- into a single view. This view will be responsible for creating the
|
and the submission -- into a single view. This view will be responsible for
|
||||||
form, validating POSTed data, and creating the new object (should it the data be
|
creating the form, validating POSTed data, and creating the new object (if the
|
||||||
valid). An added bonus of this approach is that errors and the form will both
|
data is valid). An added bonus of this approach is that errors and the form will
|
||||||
be available on the same page, so errors with fields can be presented in context.
|
both be available on the same page, so errors with fields can be presented in
|
||||||
|
context.
|
||||||
|
|
||||||
.. admonition:: Philosophy::
|
.. admonition:: Philosophy::
|
||||||
|
|
||||||
Finally, for the HTTP purists in the audience (and the authorship), this
|
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
|
nicely matches the "true" meanings of HTTP GET and HTTP POST: GET fetches
|
||||||
the form, POST creates the new object.
|
the form, and POST creates the new object.
|
||||||
|
|
||||||
Below is the finished view::
|
Below is the finished view::
|
||||||
|
|
||||||
|
@ -218,29 +223,31 @@ Below is the finished view::
|
||||||
manipulator = places.AddManipulator()
|
manipulator = places.AddManipulator()
|
||||||
|
|
||||||
if request.POST:
|
if request.POST:
|
||||||
# If data was POSTed, we're trying to create a new Place
|
# If data was POSTed, we're trying to create a new Place.
|
||||||
new_data = request.POST.copy()
|
new_data = request.POST.copy()
|
||||||
|
|
||||||
# Check for errors
|
# Check for errors.
|
||||||
errors = manipulator.get_validation_errors(new_data)
|
errors = manipulator.get_validation_errors(new_data)
|
||||||
|
|
||||||
if not errors:
|
if not errors:
|
||||||
# No errors -- this means we can save the data!
|
# No errors. This means we can save the data!
|
||||||
manipulator.do_html2python(new_data)
|
manipulator.do_html2python(new_data)
|
||||||
new_place = manipulator.save(new_data)
|
new_place = manipulator.save(new_data)
|
||||||
|
|
||||||
# Redirect to the object's "edit" page (so that reloads
|
# Redirect to the object's "edit" page. Always use a redirect
|
||||||
# don't accidentally create duplicate entries)
|
# 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)
|
return HttpResponseRedirect("/places/edit/%i/" % new_place.id)
|
||||||
else:
|
else:
|
||||||
# No POST, so we want a brand new form without any data or errors
|
# No POST, so we want a brand new form without any data or errors.
|
||||||
errors = new_data = {}
|
errors = new_data = {}
|
||||||
|
|
||||||
# Create the FormWrapper, template, context, response
|
# Create the FormWrapper, template, context, response.
|
||||||
form = formfields.FormWrapper(manipulator, new_data, errors)
|
form = formfields.FormWrapper(manipulator, new_data, errors)
|
||||||
t = template_loader.get_template("places/create_form")
|
t = template_loader.get_template("places/create_form")
|
||||||
c = Context(request, {
|
c = Context(request, {
|
||||||
'form' : form,
|
'form': form,
|
||||||
})
|
})
|
||||||
return HttpResponse(t.render(c))
|
return HttpResponse(t.render(c))
|
||||||
|
|
||||||
|
@ -287,34 +294,33 @@ and here's the ``create_form`` template::
|
||||||
The second two arguments to ``FormWrapper`` (``new_data`` and ``errors``)
|
The second two arguments to ``FormWrapper`` (``new_data`` and ``errors``)
|
||||||
deserve some mention.
|
deserve some mention.
|
||||||
|
|
||||||
The first is any "default" data to be used as values for the fields; pulling the
|
The first is any "default" data to be used as values for the fields. Pulling
|
||||||
data from ``request.POST`` as is done above makes sure that if there are errors,
|
the data from ``request.POST``, as is done above, makes sure that if there are
|
||||||
the values the user put in aren't lost. If you try the above example, you'll see
|
errors, the values the user put in aren't lost. If you try the above example,
|
||||||
this in action.
|
you'll see this in action.
|
||||||
|
|
||||||
The second argument is the error list retrieved from
|
The second argument is the error list retrieved from
|
||||||
``manipulator.get_validation_errors``. When passed into the ``FormWrapper``, this gives
|
``manipulator.get_validation_errors``. When passed into the ``FormWrapper``,
|
||||||
each field an ``errors`` item (which is a list of error messages associated with the
|
this gives each field an ``errors`` item (which is a list of error messages
|
||||||
field) as well as a ``html_error_list`` item which is a ``<ul>`` of error messages.
|
associated with the field) as well as a ``html_error_list`` item, which is a
|
||||||
The above template uses these error items to display a simple error message next
|
``<ul>`` of error messages. The above template uses these error items to
|
||||||
to each field.
|
display a simple error message next to each field.
|
||||||
|
|
||||||
Using the ``ChangeManipulator``
|
Using the ``ChangeManipulator``
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
So: the above has covered using the ``AddManipulator`` to create a new object;
|
The above has covered using the ``AddManipulator`` to create a new object. What
|
||||||
what about editing an existing one? It's rather shockingly similar to creating
|
about editing an existing one? It's shockingly similar to creating a new one::
|
||||||
a new one::
|
|
||||||
|
|
||||||
def edit_place(request, place_id):
|
def edit_place(request, place_id):
|
||||||
# Get the place in question from the database and create a ChangeManipulator
|
# Get the place in question from the database and create a
|
||||||
# at the same time
|
# ChangeManipulator at the same time.
|
||||||
try:
|
try:
|
||||||
manipulator = places.ChangeManipulator(place_id)
|
manipulator = places.ChangeManipulator(place_id)
|
||||||
except places.PlaceDoesNotExist:
|
except places.PlaceDoesNotExist:
|
||||||
raise Http404
|
raise Http404
|
||||||
|
|
||||||
# Grab the Place object is question for future use
|
# Grab the Place object is question for future use.
|
||||||
place = manipulator.original_object
|
place = manipulator.original_object
|
||||||
|
|
||||||
if request.POST:
|
if request.POST:
|
||||||
|
@ -334,46 +340,46 @@ a new one::
|
||||||
form = formfields.FormWrapper(manipulator, new_data, errors)
|
form = formfields.FormWrapper(manipulator, new_data, errors)
|
||||||
t = template_loader.get_template("places/edit_form")
|
t = template_loader.get_template("places/edit_form")
|
||||||
c = Context(request, {
|
c = Context(request, {
|
||||||
'form' : form,
|
'form': form,
|
||||||
'place' : place,
|
'place': place,
|
||||||
})
|
})
|
||||||
return HttpResponse(t.render(c))
|
return HttpResponse(t.render(c))
|
||||||
|
|
||||||
The only real differences here are:
|
The only real differences are:
|
||||||
|
|
||||||
* A ``ChangeManipulator`` instead of an ``AddManipulator`` is created;
|
* We create a ``ChangeManipulator`` instead of an ``AddManipulator``.
|
||||||
The argument to any ``ChangeManipulator`` is the id of the object
|
The argument to a ``ChangeManipulator`` is the ID of the object
|
||||||
to be changed. As you can see, the initializer will raise an
|
to be changed. As you can see, the initializer will raise an
|
||||||
``ObjectDoesNotExist`` exception if the id is invalid.
|
``ObjectDoesNotExist`` exception if the ID is invalid.
|
||||||
|
|
||||||
* ``ChangeManipulator.original_object`` stores the instance of the
|
* ``ChangeManipulator.original_object`` stores the instance of the
|
||||||
object being edited.
|
object being edited.
|
||||||
|
|
||||||
* We set ``new_data`` to the original object's ``__dict__``; this makes
|
* We set ``new_data`` to the original object's ``__dict__``. This makes
|
||||||
sure that the form fields contain the current values of the object.
|
sure the form fields contain the current values of the object.
|
||||||
``FormWrapper`` does not modify ``new_data`` in any way, and templates
|
``FormWrapper`` does not modify ``new_data`` in any way, and templates
|
||||||
cannot, so this is perfectly safe.
|
cannot, so this is perfectly safe.
|
||||||
|
|
||||||
* The above example uses a different template so that create and edit can
|
* The above example uses a different template, so create and edit can be
|
||||||
be "skinned" differently if needed, but the form chunk itself is
|
"skinned" differently if needed, but the form chunk itself is completely
|
||||||
completely identical to the one in the create form above.
|
identical to the one in the create form above.
|
||||||
|
|
||||||
The astute programmer will notice that the add and create functions are nearly
|
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
|
identical and could in fact be collapsed into a single view. This is left as an
|
||||||
as an exercise for said programmer.
|
exercise for said programmer.
|
||||||
|
|
||||||
(However, the even-more-astute programmer will take heed of the note at the top
|
(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
|
of this document and check out the `generic views`_ documentation if all she
|
||||||
wishes to do is this type of simple create/update).
|
wishes to do is this type of simple create/update.)
|
||||||
|
|
||||||
Custom forms and manipulators
|
Custom forms and manipulators
|
||||||
=============================
|
=============================
|
||||||
|
|
||||||
All the above is fine and dandy if you want to just use the automatically created
|
All the above is fine and dandy if you just want to use the automatically
|
||||||
manipulators, but the coolness doesn't end there: you can easily create your
|
created manipulators. But the coolness doesn't end there: You can easily create
|
||||||
own custom manipulators for handling custom forms.
|
your own custom manipulators for handling custom forms.
|
||||||
|
|
||||||
Custom manipulators are pretty simple; here's a manipulator that you might use
|
Custom manipulators are pretty simple. Here's a manipulator that you might use
|
||||||
for a "contact" form on a website::
|
for a "contact" form on a website::
|
||||||
|
|
||||||
from django.core import formfields
|
from django.core import formfields
|
||||||
|
@ -399,8 +405,8 @@ method of a custom manipulator is ``__init__`` which must define the fields
|
||||||
present in the manipulator. See the ``django.core.formfields`` module for
|
present in the manipulator. See the ``django.core.formfields`` module for
|
||||||
all the form fields provided by Django.
|
all the form fields provided by Django.
|
||||||
|
|
||||||
You use this custom manipulator exactly as you would use an auto-generated one;
|
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::
|
Here's a simple function that might drive the above form::
|
||||||
|
|
||||||
def contact_form(request):
|
def contact_form(request):
|
||||||
manipulator = ContactFormManipulator()
|
manipulator = ContactFormManipulator()
|
||||||
|
@ -410,7 +416,7 @@ here's a simple function that might drive the above form::
|
||||||
if not errors:
|
if not errors:
|
||||||
manipulator.do_html2python(new_data)
|
manipulator.do_html2python(new_data)
|
||||||
|
|
||||||
# send email using new_data here...
|
# Send e-mail using new_data here...
|
||||||
|
|
||||||
return HttpResponseRedirect("/contact/thankyou/")
|
return HttpResponseRedirect("/contact/thankyou/")
|
||||||
else:
|
else:
|
||||||
|
@ -418,18 +424,18 @@ here's a simple function that might drive the above form::
|
||||||
form = formfields.FormWrapper(manipulator, new_data, errors)
|
form = formfields.FormWrapper(manipulator, new_data, errors)
|
||||||
t = template_loader.get_template("contact_form")
|
t = template_loader.get_template("contact_form")
|
||||||
c = Context(request, {
|
c = Context(request, {
|
||||||
'form' : form,
|
'form': form,
|
||||||
})
|
})
|
||||||
return HttpResponse(t.render(c))
|
return HttpResponse(t.render(c))
|
||||||
|
|
||||||
Validators
|
Validators
|
||||||
==========
|
==========
|
||||||
|
|
||||||
One extremely useful feature of manipulators is the automatic validation it
|
One useful feature of manipulators is the automatic validation. Validation is
|
||||||
performs. Validation is done using a simple validation API: a validator is
|
done using a simple validation API: A validator is a callable that raises a
|
||||||
simple a callable that raises a ``ValidationError`` if there's something wrong
|
``ValidationError`` if there's something wrong with the data.
|
||||||
with the data. ``django.core.validators`` defines a whole host of validator
|
``django.core.validators`` defines a host of validator functions, but defining
|
||||||
functions, but defining your own couldn't be easier::
|
your own couldn't be easier::
|
||||||
|
|
||||||
from django.core import validators, formfields
|
from django.core import validators, formfields
|
||||||
|
|
||||||
|
@ -442,15 +448,18 @@ functions, but defining your own couldn't be easier::
|
||||||
|
|
||||||
def isValidToAddress(self, field_data, all_data):
|
def isValidToAddress(self, field_data, all_data):
|
||||||
if not field_data.endswith("@example.com"):
|
if not field_data.endswith("@example.com"):
|
||||||
raise ValidationError("You can only send messages to example.com email addresses")
|
raise 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
|
Above, we've added a "to" field to the contact form, but required that the "to"
|
||||||
"to" address end with "@example.com" by adding the ``isValidToAddress``
|
address end with "@example.com" by adding the ``isValidToAddress`` validator to
|
||||||
validator to the field's ``validator_list``.
|
the field's ``validator_list``.
|
||||||
|
|
||||||
The arguments to a validator function take a little explanation. ``field_data``
|
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 dict of all the
|
is the value of the field in question, and ``all_data`` is a dictionary of all
|
||||||
data being validated. Note that at the point validators are called all data
|
the data being validated. Note that at the point validators are called all
|
||||||
will still be strings (as ``do_html2python`` hasn't been called yet).
|
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.
|
||||||
|
|
||||||
.. _`generic views`: http://www.djangoproject.com/documentation/generic_views/
|
.. _`generic views`: http://www.djangoproject.com/documentation/generic_views/
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
Using generic views
|
Using generic views
|
||||||
===================
|
===================
|
||||||
|
|
||||||
Writing web applications can often be monotonous as we repeat certain patterns
|
Writing Web applications can be monotonous, because we repeat certain patterns
|
||||||
again and again. In Django, the most common of these patterns have been abstracted into
|
again and again. In Django, the most common of these patterns have been
|
||||||
"generic views" that let you quickly provide common views of object without actually
|
abstracted into "generic views" that let you quickly provide common views of
|
||||||
needing to write any views.
|
an object without actually needing to write any views.
|
||||||
|
|
||||||
Django's generic views contain the following:
|
Django's generic views contain the following:
|
||||||
|
|
||||||
|
@ -27,9 +27,9 @@ Django's generic views contain the following:
|
||||||
.. _latest: http://www.djangoproject.com/weblog/
|
.. _latest: http://www.djangoproject.com/weblog/
|
||||||
|
|
||||||
All of these views are used by creating configuration dictionaries in
|
All of these views are used by creating configuration dictionaries in
|
||||||
your urlconfig files and passing those dicts as the third member of the
|
your URLconf files and passing those dictionaries as the third member of the
|
||||||
urlconf tuple. For example, here's the urlconf for the simple weblog
|
URLconf tuple. For example, here's the URLconf for the simple weblog app that
|
||||||
app that drives the blog on djangoproject.com::
|
drives the blog on djangoproject.com::
|
||||||
|
|
||||||
from django.conf.urls.defaults import *
|
from django.conf.urls.defaults import *
|
||||||
|
|
||||||
|
@ -47,48 +47,50 @@ app that drives the blog on djangoproject.com::
|
||||||
(r'^/?$', 'archive_index', info_dict),
|
(r'^/?$', 'archive_index', info_dict),
|
||||||
)
|
)
|
||||||
|
|
||||||
As you can see, this urlconf defines a few options in ``info_dict`` that tell
|
As you can see, this URLconf defines a few options in ``info_dict`` that tell
|
||||||
the generic view which model to use (``blog.entries`` in this case), as well as
|
the generic view which model to use (``blog.entries`` in this case), as well as
|
||||||
some extra information.
|
some extra information.
|
||||||
|
|
||||||
Documentation of each generic view follows along with a list of all keyword arguments
|
Documentation of each generic view follows, along with a list of all keyword
|
||||||
that a generic view expects. Remember that as in the example above, arguments may
|
arguments that a generic view expects. Remember that as in the example above,
|
||||||
either come from the URL pattern (as ``month``, ``day``, ``year``, etc. do above) or
|
arguments may either come from the URL pattern (as ``month``, ``day``,
|
||||||
from the additional information dict (as for ``app_label``, ``module_name``, etc.).
|
``year``, etc. do above) or from the additional-information dictionary (as for
|
||||||
|
``app_label``, ``module_name``, etc.).
|
||||||
|
|
||||||
All the generic views that follow require the ``app_label`` and ``module_name`` keys.
|
All the generic views that follow require the ``app_label`` and ``module_name`` keys.
|
||||||
These values are easiest to explain through example::
|
These values are easiest to explain through example::
|
||||||
|
|
||||||
>>> from django.models.blog import entries
|
>>> from django.models.blog import entries
|
||||||
|
|
||||||
In the above line, ``blog`` is the ``app_label`` (this is the name of the file that
|
In the above line, ``blog`` is the ``app_label`` (the name of the file that
|
||||||
holds all your model definitions) and ``entries`` is the ``module_name`` (this is
|
holds all your model definitions) and ``entries`` is the ``module_name``
|
||||||
either a pluralized, lowercased version of the model class name or the value of
|
(either a pluralized, lowercased version of the model class name, or the value
|
||||||
the ``module_name`` option of your model). In the docs below, these keys will not
|
of the ``module_name`` option of your model). In the docs below, these keys
|
||||||
be repeated, but each generic view requires them.
|
will not be repeated, but each generic view requires them.
|
||||||
|
|
||||||
Using date-based generic views
|
Using date-based generic views
|
||||||
==============================
|
==============================
|
||||||
|
|
||||||
Date-based generic views (in the module ``django.views.generic.date_based``)
|
Date-based generic views (in the module ``django.views.generic.date_based``)
|
||||||
export six functions for dealing with date-based data. Besides ``app_label``
|
feature six functions for dealing with date-based data. Besides ``app_label``
|
||||||
and ``module_name``, all date-based generic views require that the ``date_field``
|
and ``module_name``, all date-based generic views require that the
|
||||||
argument to passed to them; this is the name of the field that stores the date
|
``date_field`` argument be passed to them. This is the name of the field that
|
||||||
the objects should key off of.
|
stores the date the objects should key off of.
|
||||||
|
|
||||||
Additional, all date-based generic views have the following optional arguments:
|
Additionally, all date-based generic views have the following optional
|
||||||
|
arguments:
|
||||||
|
|
||||||
======================= ==================================================
|
======================= ==================================================
|
||||||
Argument Description
|
Argument Description
|
||||||
======================= ==================================================
|
======================= ==================================================
|
||||||
``template_name`` Override the default template name used for the
|
``template_name`` Overrides the default template name used for the
|
||||||
view.
|
view.
|
||||||
|
|
||||||
``extra_lookup_kwargs`` A dictionary of extra lookup parameters (see
|
``extra_lookup_kwargs`` A dictionary of extra lookup parameters (see
|
||||||
the `database API docs`_).
|
the `database API docs`_).
|
||||||
|
|
||||||
``extra_context`` A dict of extra data to put into the template's
|
``extra_context`` A dictionary of extra data to put into the
|
||||||
context.
|
template's context.
|
||||||
======================= ==================================================
|
======================= ==================================================
|
||||||
|
|
||||||
.. _`database API docs`: http://www.djangoproject.com/documentation/db_api/
|
.. _`database API docs`: http://www.djangoproject.com/documentation/db_api/
|
||||||
|
@ -96,9 +98,9 @@ Additional, all date-based generic views have the following optional arguments:
|
||||||
The date-based generic functions are:
|
The date-based generic functions are:
|
||||||
|
|
||||||
``archive_index``
|
``archive_index``
|
||||||
A top-level index page showing the "latest" objects. Has an optional argument,
|
A top-level index page showing the "latest" objects. Has an optional
|
||||||
``num_latest`` which is the number of items to display on the page (defaults
|
argument, ``num_latest``, which is the number of items to display on the
|
||||||
to 15).
|
page (defaults to 15).
|
||||||
|
|
||||||
Uses the template ``app_label/module_name_archive`` by default.
|
Uses the template ``app_label/module_name_archive`` by default.
|
||||||
|
|
||||||
|
@ -118,56 +120,56 @@ The date-based generic functions are:
|
||||||
Has the following template context:
|
Has the following template context:
|
||||||
|
|
||||||
``date_list``
|
``date_list``
|
||||||
List of months in this year with objects
|
List of months in the given year with objects
|
||||||
``year``
|
``year``
|
||||||
This year
|
The given year (an integer)
|
||||||
|
|
||||||
``archive_month``
|
``archive_month``
|
||||||
Monthly archive; requires that ``year`` and ``month`` arguments be given.
|
Monthly archive. Requires that ``year`` and ``month`` arguments be given.
|
||||||
|
|
||||||
Uses the template ``app_label/module_name__archive_month`` by default.
|
Uses the template ``app_label/module_name__archive_month`` by default.
|
||||||
|
|
||||||
Has the following template context:
|
Has the following template context:
|
||||||
|
|
||||||
``month``
|
``month``
|
||||||
(datetime object) this month
|
The given month (a datetime.datetime object)
|
||||||
``object_list``
|
``object_list``
|
||||||
list of objects published in the given month
|
List of objects published in the given month
|
||||||
|
|
||||||
``archive_day``
|
``archive_day``
|
||||||
Daily archive; requires that ``year``, ``month``, and ``day`` arguments
|
Daily archive. Requires that ``year``, ``month``, and ``day`` arguments be
|
||||||
be given.
|
given.
|
||||||
|
|
||||||
Uses the template ``app_label/module_name__archive_day`` by default.
|
Uses the template ``app_label/module_name__archive_day`` by default.
|
||||||
|
|
||||||
Has the following template context:
|
Has the following template context:
|
||||||
|
|
||||||
``object_list``
|
``object_list``
|
||||||
list of objects published this day
|
List of objects published this day
|
||||||
``day``
|
``day``
|
||||||
(datetime) the day
|
The given day (a datetime.datetime object)
|
||||||
``previous_day``
|
``previous_day``
|
||||||
(datetime) the previous day
|
The previous day (a datetime.datetime object)
|
||||||
``next_day``
|
``next_day``
|
||||||
(datetime) the next day, or None if the current day is today
|
The next day (a datetime.datetime object), or None if the given
|
||||||
|
day is today
|
||||||
|
|
||||||
``archive_today``
|
``archive_today``
|
||||||
List of objects for today; exactly the same as ``archive_day``, except
|
List of objects for today. Exactly the same as ``archive_day``, except
|
||||||
that the year/month/day arguments are not given and today's date is
|
the year/month/day arguments are not given, and today's date is used
|
||||||
used instead.
|
instead.
|
||||||
|
|
||||||
``object_detail``
|
``object_detail``
|
||||||
Individual object page; requires ``year``/``month``/``day`` arguments like
|
Individual object page. Requires ``year``/``month``/``day`` arguments like
|
||||||
``archive_day``. This function can be used with two types of URLs: either
|
``archive_day``. This function can be used with two types of URLs: either
|
||||||
``/year/month/day/slug/`` or ``/year/month/day/object_id/``.
|
``/year/month/day/slug/`` or ``/year/month/day/object_id/``.
|
||||||
|
|
||||||
If you're using the slug-style URLs, you'll need to have a ``slug`` item in
|
If you're using the slug-style URLs, you'll need to have a ``slug`` item in
|
||||||
your urlconf, and you'll need to pass a ``slug_field`` key in your info
|
your URLconf, and you'll need to pass a ``slug_field`` key in your info
|
||||||
dict to indicate the name of the slug field.
|
dictionary to indicate the name of the slug field.
|
||||||
|
|
||||||
If your using the object_id-style URLs, you'll just need to have the URL
|
If your using the object_id-style URLs, you'll just need to give the URL
|
||||||
pattern have an ``object_id`` field.
|
pattern an ``object_id`` field.
|
||||||
|
|
||||||
You can also pass the ``template_name_field`` argument to indicate that the
|
You can also pass the ``template_name_field`` argument to indicate that the
|
||||||
the object stores the name of its template in a field on the object itself.
|
the object stores the name of its template in a field on the object itself.
|
||||||
|
@ -175,11 +177,11 @@ The date-based generic functions are:
|
||||||
Using list/detail generic views
|
Using list/detail generic views
|
||||||
===============================
|
===============================
|
||||||
|
|
||||||
The list-detail generic views (in the ``django.views.generic.list_detail`` module)
|
The list-detail generic views (in the ``django.views.generic.list_detail``
|
||||||
are similar to the data-based ones, except the list-detail views simply have two
|
module) are similar to the data-based ones, except the list-detail views simply
|
||||||
views: a list of objects, and an individual object page.
|
have two views: a list of objects, and an individual object page.
|
||||||
|
|
||||||
All these views take the same three optional arguments as the date-based ones do
|
All these views take the same three optional arguments as the date-based ones
|
||||||
(and they obviously do not accept or require the date field argument).
|
(and they obviously do not accept or require the date field argument).
|
||||||
|
|
||||||
Individual views are:
|
Individual views are:
|
||||||
|
@ -197,9 +199,9 @@ Individual views are:
|
||||||
The view will expect a ``page`` GET param with
|
The view will expect a ``page`` GET param with
|
||||||
the (zero-indexed) page number.
|
the (zero-indexed) page number.
|
||||||
|
|
||||||
``allow_empty`` If ``False`` and there are no objects to display
|
``allow_empty`` If ``False`` and there are no objects to display,
|
||||||
the view will raise a 404 instead of displaying
|
the view will raise a 404 instead of displaying
|
||||||
an empty index page.
|
an empty index page. ``False`` is default.
|
||||||
======================= =================================================
|
======================= =================================================
|
||||||
|
|
||||||
Uses the template ``app_label/module_name__list`` by default.
|
Uses the template ``app_label/module_name__list`` by default.
|
||||||
|
@ -207,60 +209,61 @@ Individual views are:
|
||||||
Has the following template context:
|
Has the following template context:
|
||||||
|
|
||||||
``object_list``
|
``object_list``
|
||||||
list of objects
|
List of objects
|
||||||
``is_paginated``
|
``is_paginated``
|
||||||
are the results paginated?
|
Are the results paginated? Either True or False
|
||||||
|
|
||||||
If the results are paginated, the context will have some extra variables:
|
If the results are paginated, the context will have some extra variables:
|
||||||
|
|
||||||
``results_per_page``
|
``results_per_page``
|
||||||
number of objects per page
|
Number of objects per page
|
||||||
``has_next``
|
``has_next``
|
||||||
is there a next page?
|
Is there a next page?
|
||||||
``has_previous``
|
``has_previous``
|
||||||
is there a prev page?
|
Is there a previous page?
|
||||||
``page``
|
``page``
|
||||||
the current page
|
The current page number
|
||||||
``next``
|
``next``
|
||||||
the next page
|
The next page number
|
||||||
``previous``
|
``previous``
|
||||||
the previous page
|
The previous page
|
||||||
``pages``
|
``pages``
|
||||||
number of pages, total
|
Number of pages total
|
||||||
|
|
||||||
``object_detail``
|
``object_detail``
|
||||||
Object detail page. This works like and takes the same arguments as
|
Object detail page. This works like and takes the same arguments as
|
||||||
the date-based ``object_detail`` above, except this one obviously
|
the date-based ``object_detail`` above, except this one, obviously,
|
||||||
does not take the year/month/day arguments.
|
does not take the year/month/day arguments.
|
||||||
|
|
||||||
Using create/update/delete generic views
|
Using create/update/delete generic views
|
||||||
========================================
|
========================================
|
||||||
|
|
||||||
The ``django.views.generic.create_update`` module contains a set of functions
|
The ``django.views.generic.create_update`` module contains a set of functions
|
||||||
for creating, editing, and deleting objects. These views take the same global
|
for creating, editing and deleting objects. These views take the same global
|
||||||
arguments as the above sets of generic views; they also have a
|
arguments as the above sets of generic views. They also have a
|
||||||
``login_required`` argument which, if ``True``, requires the user to be logged
|
``login_required`` argument which, if ``True``, requires the user to be logged
|
||||||
in to have access to the page (``login_required`` defaults to ``False``).
|
in to have access to the page. (``login_required`` defaults to ``False``.)
|
||||||
|
|
||||||
The create/update/delete views are:
|
The create/update/delete views are:
|
||||||
|
|
||||||
``create_object``
|
``create_object``
|
||||||
Create a new object. Has an extra optional argument, ``post_save_redirect``,
|
Create a new object. Has an extra optional argument, ``post_save_redirect``,
|
||||||
which is a URL that the view will redirect to after saving the object
|
which is a URL to which the view will redirect after saving the object.
|
||||||
(defaults to ``object.get_absolute_url()``).
|
It defaults to ``object.get_absolute_url()``.
|
||||||
|
|
||||||
``post_save_redirect`` may contain dictionary string formatting which will
|
``post_save_redirect`` may contain dictionary string formatting, which will
|
||||||
be interpolated against the object's dict (so you could use
|
be interpolated against the object's field attributes. For example, you
|
||||||
``post_save_redirect="/polls/%(slug)s/"``, for example).
|
could use ``post_save_redirect="/polls/%(slug)s/"``.
|
||||||
|
|
||||||
Uses the template ``app_label/module_name__form`` by default (this is the
|
Uses the template ``app_label/module_name__form`` by default. This is the
|
||||||
same template as the ``update_object`` view below; your template can tell
|
same template as the ``update_object`` view below. Your template can tell
|
||||||
the different by the presence or absence of ``{{ object }}`` in the context.
|
the different by the presence or absence of ``{{ object }}`` in the
|
||||||
|
context.
|
||||||
|
|
||||||
Has the following template context:
|
Has the following template context:
|
||||||
|
|
||||||
form
|
form
|
||||||
the form wrapper for the object
|
The form wrapper for the object
|
||||||
|
|
||||||
.. admonition:: Note
|
.. admonition:: Note
|
||||||
|
|
||||||
|
@ -271,31 +274,32 @@ The create/update/delete views are:
|
||||||
|
|
||||||
``update_object``
|
``update_object``
|
||||||
Edit an existing object. Has the same extra slug/ID parameters as
|
Edit an existing object. Has the same extra slug/ID parameters as
|
||||||
``list_detail.object_detail`` does (see above), and the same ``post_save_redirect``
|
``list_detail.object_detail`` does (see above), and the same
|
||||||
as ``create_object`` does.
|
``post_save_redirect`` as ``create_object`` does.
|
||||||
|
|
||||||
Uses the template ``app_label/module_name__form`` by default.
|
Uses the template ``app_label/module_name__form`` by default.
|
||||||
|
|
||||||
Has the following template context:
|
Has the following template context:
|
||||||
|
|
||||||
form
|
form
|
||||||
the form wrapper for the object
|
The form wrapper for the object
|
||||||
object
|
object
|
||||||
the original object being edited
|
The original object being edited
|
||||||
|
|
||||||
``delete_object``
|
``delete_object``
|
||||||
Delete an existing object. The given object will only actually be deleted if
|
Delete an existing object. The given object will only actually be deleted
|
||||||
the request method is POST; if this view is fetched with GET it will display
|
if the request method is POST. If this view is fetched with GET, it will
|
||||||
a confirmation page that should contain a form that POSTs to the same URL.
|
display a confirmation page that should contain a form that POSTs to the
|
||||||
|
same URL.
|
||||||
|
|
||||||
You must provide the ``post_delete_redirect`` argument to this function so
|
You must provide the ``post_delete_redirect`` argument to this function, so
|
||||||
that the view knows where to go after the object is deleted.
|
that the view knows where to go after the object is deleted.
|
||||||
|
|
||||||
If fetched with GET, uses the template
|
If fetched with GET, it uses the template
|
||||||
``app_label/module_name_s_confirm_delete`` by default (uses no template if
|
``app_label/module_name_s_confirm_delete`` by default. It uses no template
|
||||||
POSTed; simply deletes the object).
|
if POSTed -- it simply deletes the object and redirects.
|
||||||
|
|
||||||
Has the following template context:
|
Has the following template context:
|
||||||
|
|
||||||
object
|
object
|
||||||
the object about to be deleted
|
The object about to be deleted
|
||||||
|
|
Loading…
Reference in New Issue