Django

Code

root/django/branches/0.95-bugfixes/docs/forms.txt

Revision 3475, 24.4 kB (checked in by adrian, 2 years ago)

Fixed ReST error in docs/forms.txt

Line 
1 ===============================
2 Forms, fields, and manipulators
3 ===============================
4
5 Once you've got a chance to play with Django's admin interface, you'll probably
6 wonder if the fantastic form validation framework it uses is available to user
7 code. It is, and this document explains how the framework works.
8
9     .. admonition:: A note to the lazy
10
11         If all you want to do is present forms for a user to create and/or
12         update a given object, you may be able to use `generic views`_.
13
14 We'll take a top-down approach to examining Django's form validation framework,
15 because much of the time you won't need to use the lower-level APIs. Throughout
16 this document, we'll be working with the following model, a "place" object::
17
18     from django.db import models
19
20     PLACE_TYPES = (
21         (1, 'Bar'),
22         (2, 'Restaurant'),
23         (3, 'Movie Theater'),
24         (4, 'Secret Hideout'),
25     )
26
27     class Place(models.Model):
28         name = models.CharField(maxlength=100)
29         address = models.CharField(maxlength=100, blank=True)
30         city = models.CharField(maxlength=50, blank=True)
31         state = models.USStateField()
32         zip_code = models.CharField(maxlength=5, blank=True)
33         place_type = models.IntegerField(choices=PLACE_TYPES)
34
35         class Admin:
36             pass
37
38         def __str__(self):
39             return self.name
40
41 Defining the above class is enough to create an admin interface to a ``Place``,
42 but what if you want to allow public users to submit places?
43
44 Manipulators
45 ============
46
47 The highest-level interface for object creation and modification is the
48 **Manipulator** framework. A manipulator is a utility class tied to a given
49 model that "knows" how to create or modify instances of that model and how to
50 validate data for the object. Manipulators come in two flavors:
51 ``AddManipulators`` and ``ChangeManipulators``. Functionally they are quite
52 similar, but the former knows how to create new instances of the model, while
53 the latter modifies existing instances. Both types of classes are automatically
54 created when you define a new class::
55
56     >>> from mysite.myapp.models import Place
57     >>> Place.AddManipulator
58     <class 'django.models.manipulators.AddManipulator'>
59     >>> Place.ChangeManipulator
60     <class 'django.models.manipulators.ChangeManipulator'>
61
62 Using the ``AddManipulator``
63 ----------------------------
64
65 We'll start with the ``AddManipulator``.  Here's a very simple view that takes
66 POSTed data from the browser and creates a new ``Place`` object::
67
68     from django.shortcuts import render_to_response
69     from django.http import Http404, HttpResponse, HttpResponseRedirect
70     from django import forms
71     from mysite.myapp.models import Place
72
73     def naive_create_place(request):
74         """A naive approach to creating places; don't actually use this!"""
75         # Create the AddManipulator.
76         manipulator = Place.AddManipulator()
77
78         # Make a copy of the POSTed data so that do_html2python can
79         # modify it in place (request.POST is immutable).
80         new_data = request.POST.copy()
81
82         # Convert the request data (which will all be strings) into the
83         # appropriate Python types for those fields.
84         manipulator.do_html2python(new_data)
85
86         # Save the new object.
87         new_place = manipulator.save(new_data)
88
89         # It worked!
90         return HttpResponse("Place created: %s" % new_place)
91
92 The ``naive_create_place`` example works, but as you probably can tell, this
93 view has a number of problems:
94
95     * No validation of any sort is performed. If, for example, the ``name`` field
96       isn't given in ``request.POST``, the save step will cause a database error
97       because that field is required. Ugly.
98
99     * Even if you *do* perform validation, there's still no way to give that
100       information to the user in any sort of useful way.
101
102     * You'll have to separately create a form (and view) that submits to this
103       page, which is a pain and is redundant.
104
105 Let's dodge these problems momentarily to take a look at how you could create a
106 view with a form that submits to this flawed creation view::
107
108     def naive_create_place_form(request):
109         """Simplistic place form view; don't actually use anything like this!"""
110         # Create a FormWrapper object that the template can use. Ignore
111         # the last two arguments to FormWrapper for now.
112         form = forms.FormWrapper(Place.AddManipulator(), {}, {})
113         return render_to_response('places/naive_create_form.html', {'form': form})
114
115 (This view, as well as all the following ones, has the same imports as in the
116 first example above.)
117
118 The ``forms.FormWrapper`` object is a wrapper that templates can
119 easily deal with to create forms. Here's the ``naive_create_form.html``
120 template::
121
122     {% extends "base.html" %}
123
124     {% block content %}
125     <h1>Create a place:</h1>
126
127     <form method="post" action="../do_new/">
128     <p><label for="id_name">Name:</label> {{ form.name }}</p>
129     <p><label for="id_address">Address:</label> {{ form.address }}</p>
130     <p><label for="id_city">City:</label> {{ form.city }}</p>
131     <p><label for="id_state">State:</label> {{ form.state }}</p>
132     <p><label for="id_zip_code">Zip:</label> {{ form.zip_code }}</p>
133     <p><label for="id_place_type">Place type:</label> {{ form.place_type }}</p>
134     <input type="submit" />
135     </form>
136     {% endblock %}
137
138 Before we get back to the problems with these naive set of views, let's go over
139 some salient points of the above template::
140
141     * Field "widgets" are handled for you: ``{{ form.field }}`` automatically
142       creates the "right" type of widget for the form, as you can see with the
143       ``place_type`` field above.
144
145     * There isn't a way just to spit out the form. You'll still need to define
146       how the form gets laid out. This is a feature: Every form should be
147       designed differently. Django doesn't force you into any type of mold.
148       If you must use tables, use tables. If you're a semantic purist, you can
149       probably find better HTML than in the above template.
150
151     * To avoid name conflicts, the ``id``s of form elements take the form
152       "id_*fieldname*".
153
154 By creating a creation form we've solved problem number 3 above, but we still
155 don't have any validation. Let's revise the validation issue by writing a new
156 creation view that takes validation into account::
157
158     def create_place_with_validation(request):
159         manipulator = Place.AddManipulator()
160         new_data = request.POST.copy()
161
162         # Check for validation errors
163         errors = manipulator.get_validation_errors(new_data)
164         if errors:
165             return render_to_response('places/errors.html', {'errors': errors})
166         else:
167             manipulator.do_html2python(new_data)
168             new_place = manipulator.save(new_data)
169             return HttpResponse("Place created: %s" % new_place)
170
171 In this new version, errors will be found -- ``manipulator.get_validation_errors``
172 handles all the validation for you -- and those errors can be nicely presented
173 on an error page (templated, of course)::
174
175     {% extends "base.html" %}
176
177     {% block content %}
178
179     <h1>Please go back and correct the following error{{ errors|pluralize }}:</h1>
180     <ul>
181         {% for e in errors.items %}
182         <li>Field "{{ e.0 }}": {{ e.1|join:", " }}</li>
183         {% endfor %}
184     </ul>
185
186     {% endblock %}
187
188 Still, this has its own problems:
189
190     * There's still the issue of creating a separate (redundant) view for the
191       submission form.
192
193     * Errors, though nicely presented, are on a separate page, so the user will
194       have to use the "back" button to fix errors. That's ridiculous and unusable.
195
196 The best way to deal with these issues is to collapse the two views -- the form
197 and the submission -- into a single view.  This view will be responsible for
198 creating the form, validating POSTed data, and creating the new object (if the
199 data is valid). An added bonus of this approach is that errors and the form will
200 both be available on the same page, so errors with fields can be presented in
201 context.
202
203 .. admonition:: Philosophy:
204
205     Finally, for the HTTP purists in the audience (and the authorship), this
206     nicely matches the "true" meanings of HTTP GET and HTTP POST: GET fetches
207     the form, and POST creates the new object.
208
209 Below is the finished view::
210
211     def create_place(request):
212         manipulator = Place.AddManipulator()
213
214         if request.POST:
215             # If data was POSTed, we're trying to create a new Place.
216             new_data = request.POST.copy()
217
218             # Check for errors.
219             errors = manipulator.get_validation_errors(new_data)
220
221             if not errors:
222                 # No errors. This means we can save the data!
223                 manipulator.do_html2python(new_data)
224                 new_place = manipulator.save(new_data)
225
226                 # Redirect to the object's "edit" page. Always use a redirect
227                 # after POST data, so that reloads don't accidently create
228                 # duplicate entires, and so users don't see the confusing
229                 # "Repost POST data?" alert box in their browsers.
230                 return HttpResponseRedirect("/places/edit/%i/" % new_place.id)
231         else:
232             # No POST, so we want a brand new form without any data or errors.
233             errors = new_data = {}
234
235         # Create the FormWrapper, template, context, response.
236         form = forms.FormWrapper(manipulator, new_data, errors)
237         return render_to_response('places/create_form.html', {'form': form})
238
239 and here's the ``create_form`` template::
240
241     {% extends "base.html" %}
242
243     {% block content %}
244     <h1>Create a place:</h1>
245
246     {% if form.has_errors %}
247     <h2>Please correct the following error{{ form.error_dict|pluralize }}:</h2>
248     {% endif %}
249
250     <form method="post" action=".">
251     <p>
252         <label for="id_name">Name:</label> {{ form.name }}
253         {% if form.name.errors %}*** {{ form.name.errors|join:", " }}{% endif %}
254     </p>
255     <p>
256         <label for="id_address">Address:</label> {{ form.address }}
257         {% if form.address.errors %}*** {{ form.address.errors|join:", " }}{% endif %}
258     </p>
259     <p>
260         <label for="id_city">City:</label> {{ form.city }}
261         {% if form.city.errors %}*** {{ form.city.errors|join:", " }}{% endif %}
262     </p>
263     <p>
264         <label for="id_state">State:</label> {{ form.state }}
265         {% if form.state.errors %}*** {{ form.state.errors|join:", " }}{% endif %}
266     </p>
267     <p>
268         <label for="id_zip_code">Zip:</label> {{ form.zip_code }}
269         {% if form.zip_code.errors %}*** {{ form.zip_code.errors|join:", " }}{% endif %}
270     </p>
271     <p>
272         <label for="id_place_type">Place type:</label> {{ form.place_type }}
273         {% if form.place_type.errors %}*** {{ form.place_type.errors|join:", " }}{% endif %}
274     </p>
275     <input type="submit" />
276     </form>
277     {% endblock %}
278
279 The second two arguments to ``FormWrapper`` (``new_data`` and ``errors``)
280 deserve some mention.
281
282 The first is any "default" data to be used as values for the fields. Pulling
283 the data from ``request.POST``, as is done above, makes sure that if there are
284 errors, the values the user put in aren't lost. If you try the above example,
285 you'll see this in action.
286
287 The second argument is the error list retrieved from
288 ``manipulator.get_validation_errors``.  When passed into the ``FormWrapper``,
289 this gives each field an ``errors`` item (which is a list of error messages
290 associated with the field) as well as a ``html_error_list`` item, which is a
291 ``<ul>`` of error messages. The above template uses these error items to
292 display a simple error message next to each field. The error list is saved as
293 an ``error_dict`` attribute of the ``FormWrapper`` object.
294
295 Using the ``ChangeManipulator``
296 -------------------------------
297
298 The above has covered using the ``AddManipulator`` to create a new object. What
299 about editing an existing one? It's shockingly similar to creating a new one::
300
301     def edit_place(request, place_id):
302         # Get the place in question from the database and create a
303         # ChangeManipulator at the same time.
304         try:
305             manipulator = Place.ChangeManipulator(place_id)
306         except Place.DoesNotExist:
307             raise Http404
308
309         # Grab the Place object in question for future use.
310         place = manipulator.original_object
311
312         if request.POST:
313             new_data = request.POST.copy()
314             errors = manipulator.get_validation_errors(new_data)
315             if not errors:
316                 manipulator.do_html2python(new_data)
317                 manipulator.save(new_data)
318
319                 # Do a post-after-redirect so that reload works, etc.
320                 return HttpResponseRedirect("/places/edit/%i/" % place.id)
321         else:
322             errors = {}
323             # This makes sure the form accurate represents the fields of the place.
324             new_data = place.__dict__
325
326         form = forms.FormWrapper(manipulator, new_data, errors)
327         return render_to_response('places/edit_form.html', {'form': form, 'place': place})
328
329 The only real differences are:
330
331     * We create a ``ChangeManipulator`` instead of an ``AddManipulator``.
332       The argument to a ``ChangeManipulator`` is the ID of the object
333       to be changed. As you can see, the initializer will raise an
334       ``ObjectDoesNotExist`` exception if the ID is invalid.
335
336     * ``ChangeManipulator.original_object`` stores the instance of the
337       object being edited.
338
339     * We set ``new_data`` to the original object's ``__dict__``. This makes
340       sure the form fields contain the current values of the object.
341       ``FormWrapper`` does not modify ``new_data`` in any way, and templates
342       cannot, so this is perfectly safe.
343
344     * The above example uses a different template, so create and edit can be
345       "skinned" differently if needed, but the form chunk itself is completely
346       identical to the one in the create form above.
347
348 The astute programmer will notice the add and create functions are nearly
349 identical and could in fact be collapsed into a single view. This is left as an
350 exercise for said programmer.
351
352 (However, the even-more-astute programmer will take heed of the note at the top
353 of this document and check out the `generic views`_ documentation if all she
354 wishes to do is this type of simple create/update.)
355
356 Custom forms and manipulators
357 =============================
358
359 All the above is fine and dandy if you just want to use the automatically
360 created manipulators. But the coolness doesn't end there: You can easily create
361 your own custom manipulators for handling custom forms.
362
363 Custom manipulators are pretty simple. Here's a manipulator that you might use
364 for a "contact" form on a website::
365
366     from django import forms
367
368     urgency_choices = (
369         (1, "Extremely urgent"),
370         (2, "Urgent"),
371         (3, "Normal"),
372         (4, "Unimportant"),
373     )
374
375     class ContactManipulator(forms.Manipulator):
376         def __init__(self):
377             self.fields = (
378                 forms.EmailField(field_name="from", is_required=True),
379                 forms.TextField(field_name="subject", length=30, maxlength=200, is_required=True),
380                 forms.SelectField(field_name="urgency", choices=urgency_choices),
381                 forms.LargeTextField(field_name="contents", is_required=True),
382             )
383
384 A certain similarity to Django's models should be apparent. The only required
385 method of a custom manipulator is ``__init__`` which must define the fields
386 present in the manipulator.  See the ``django.forms`` module for
387 all the form fields provided by Django.
388
389 You use this custom manipulator exactly as you would use an auto-generated one.
390 Here's a simple function that might drive the above form::
391
392     def contact_form(request):
393         manipulator = ContactManipulator()
394         if request.POST:
395             new_data = request.POST.copy()
396             errors = manipulator.get_validation_errors(new_data)
397             if not errors:
398                 manipulator.do_html2python(new_data)
399
400                 # Send e-mail using new_data here...
401
402                 return HttpResponseRedirect("/contact/thankyou/")
403         else:
404             errors = new_data = {}
405         form = forms.FormWrapper(manipulator, new_data, errors)
406         return render_to_response('contact_form.html', {'form': form})
407        
408 ``FileField`` and ``ImageField`` special cases
409 ==============================================
410
411 Dealing with ``FileField`` and ``ImageField`` objects is a little more
412 complicated.
413
414 First, you'll need to make sure that your ``<form>`` element correctly defines
415 the ``enctype`` as ``"multipart/form-data"``, in order to upload files::
416
417   <form enctype="multipart/form-data" method="post" action="/foo/">
418
419 Next, you'll need to treat the field in the template slightly differently. A
420 ``FileField`` or ``ImageField`` is represented by *two* HTML form elements.
421
422 For example, given this field in a model::
423
424    photo = model.ImageField('/path/to/upload/location')
425
426 You'd need to display two formfields in the template::
427
428    <p><label for="id_photo">Photo:</label> {{ form.photo }}{{ form.photo_file }}</p>
429
430 The first bit (``{{ form.photo }}``) displays the currently-selected file,
431 while the second (``{{ form.photo_file }}``) actually contains the file upload
432 form field. Thus, at the validation layer you need to check the ``photo_file``
433 key.
434
435 Finally, in your view, make sure to access ``request.FILES``, rather than
436 ``request.POST``, for the uploaded files. This is necessary because
437 ``request.POST`` does not contain file-upload data.
438
439 For example, following the ``new_data`` convention, you might do something like
440 this::
441
442    new_data = request.POST.copy()
443    new_data.update(request.FILES)
444
445 Validators
446 ==========
447
448 One useful feature of manipulators is the automatic validation. Validation is
449 done using a simple validation API: A validator is a callable that raises a
450 ``ValidationError`` if there's something wrong with the data.
451 ``django.core.validators`` defines a host of validator functions (see below),
452 but defining your own couldn't be easier::
453
454     from django.core import validators
455     from django import forms
456
457     class ContactManipulator(forms.Manipulator):
458         def __init__(self):
459             self.fields = (
460                 # ... snip fields as above ...
461                 forms.EmailField(field_name="to", validator_list=[self.isValidToAddress])
462             )
463
464         def isValidToAddress(self, field_data, all_data):
465             if not field_data.endswith("@example.com"):
466                 raise validators.ValidationError("You can only send messages to example.com e-mail addresses.")
467
468 Above, we've added a "to" field to the contact form, but required that the "to"
469 address end with "@example.com" by adding the ``isValidToAddress`` validator to
470 the field's ``validator_list``.
471
472 The arguments to a validator function take a little explanation.  ``field_data``
473 is the value of the field in question, and ``all_data`` is a dictionary of all
474 the data being validated.
475
476 .. admonition:: Note::
477
478     At the point validators are called all data will still be
479     strings (as ``do_html2python`` hasn't been called yet).
480
481 Also, because consistency in user interfaces is important, we strongly urge you
482 to put punctuation at the end of your validation messages.
483
484 Ready-made Validators
485 ---------------------
486
487 Writing your own validator is not difficult, but there are some situations
488 that come up over and over again. Django comes with a number of validators
489 that can be used directly in your code. All of these functions and classes
490 reside in ``django/core/validators.py``.
491
492 The following validators should all be self-explanatory. Each one provides a
493 check for the given property:
494
495     * isAlphaNumeric
496     * isAlphaNumericURL
497     * isSlug
498     * isLowerCase
499     * isUpperCase
500     * isCommaSeparatedIntegerList
501     * isCommaSeparatedEmailList
502     * isValidIPAddress4
503     * isNotEmpty
504     * isOnlyDigits
505     * isNotOnlyDigits
506     * isInteger
507     * isOnlyLetters
508     * isValidANSIDate
509     * isValidANSITime
510     * isValidEmail
511     * isValidImage
512     * isValidImageURL
513     * isValidPhone
514     * isValidQuicktimeVideoURL
515     * isValidURL
516     * isValidHTML
517     * isWellFormedXml
518     * isWellFormedXmlFragment
519     * isExistingURL
520     * isValidUSState
521     * hasNoProfanities
522
523 There are also a group of validators that are slightly more flexible. For
524 these validators, you create a validator instance, passing in the parameters
525 described below. The returned object is a callable that can be used as a
526 validator.
527
528 For example::
529
530     from django.core import validators
531     from django import forms
532
533     power_validator = validators.IsAPowerOf(2)
534
535     class InstallationManipulator(forms.Manipulator)
536         def __init__(self):
537             self.fields = (
538                 ...
539                 forms.IntegerField(field_name = "size", validator_list=[power_validator])
540             )
541
542 Here, ``validators.IsAPowerOf(...)`` returned something that could be used as
543 a validator (in this case, a check that a number was a power of 2).
544
545 Each of the standard validators that take parameters have an optional final
546 argument (``error_message``) that is the message returned when validation
547 fails. If no message is passed in, a default message is used.
548
549 ``AlwaysMatchesOtherField``
550     Takes a field name and the current field is valid if and only if its value
551     matches the contents of the other field.
552
553 ``ValidateIfOtherFieldEquals``
554     Takes three parameters: ``other_field``, ``other_value`` and
555     ``validator_list``, in that order. If ``other_field`` has a value of
556     ``other_vaue``, then the validators in ``validator_list`` are all run
557     against the current field.
558
559 ``RequiredIfOtherFieldNotGiven``
560     Takes the name of the other field and this field is only required if the
561     other field has no value.
562
563 ``RequiredIfOtherFieldsNotGiven``
564     Similar to ``RequiredIfOtherFieldNotGiven``, except that it takes a list
565     of field names and if any one of the supplied fields does not have a value
566     provided, the field being validated is required.
567
568 ``RequiredIfOtherFieldEquals`` and ``RequiredIfOtherFieldDoesNotEqual``
569     Each of these validator classes takes a field name and a value (in that
570     order). If the given field does (or does not have, in the latter case) the
571     given value, then the current field being validated is required.
572
573     Note that because validators are called before any ``do_html2python()``
574     functions, the value being compared against is a string. So
575     ``RequiredIfOtherFieldEquals('choice', '1')`` is correct, whilst
576     ``RequiredIfOtherFieldEquals('choice', 1)`` will never result in the
577     equality test succeeding.
578
579 ``IsLessThanOtherField``
580     Takes a field name and validates that the current field being validated
581     has a value that is less than (or equal to) the other field's value.
582     Again, comparisons are done using strings, so be cautious about using
583     this function to compare data that should be treated as another type. The
584     string "123" is less than the string "2", for example. If you don't want
585     string comparison here, you will need to write your own validator.
586
587 ``IsAPowerOf``
588     Takes an integer argument and when called as a validator, checks that the
589     field being validated is a power of the integer.
590
591 ``IsValidFloat``
592     Takes a maximum number of digits and number of decimal places (in that
593     order) and validates whether the field is a float with less than the
594     maximum number of digits and decimal place.
595
596 ``MatchesRegularExpression``
597     Takes a regular expression (a string) as a parameter and validates the
598     field value against it.
599
600 ``AnyValidator``
601     Takes a list of validators as a parameter. At validation time, if the
602     field successfully validates against any one of the validators, it passes
603     validation. The validators are tested in the order specified in the
604     original list.
605
606 ``URLMimeTypeCheck``
607     Used to validate URL fields. Takes a list of MIME types (such as
608     ``text/plain``) at creation time. At validation time, it verifies that the
609     field is indeed a URL and then tries to retrieve the content at the URL.
610     Validation succeeds if the content could be retrieved and it has a content
611     type from the list used to create the validator.
612
613 ``RelaxNGCompact``
614     Used to validate an XML document against a Relax NG compact schema. Takes
615     a file path to the location of the schema and an optional root element
616     (which is wrapped around the XML fragment before validation, if supplied).
617     At validation time, the XML fragment is validated against the schema using
618     the executable specified in the ``JING_PATH`` setting (see the settings_
619     document for more details).
620
621 .. _`generic views`: http://www.djangoproject.com/documentation/generic_views/
622 .. _`models API`: http://www.djangoproject.com/documentation/model_api/
623 .. _settings: http://www.djangoproject.com/documentation/settings/
Note: See TracBrowser for help on using the browser.