Ticket #1703: forms.txt.diff

File forms.txt.diff, 11.9 KB (added by Malcolm Tredinnick <malcolm@…>, 13 years ago)

Updated documentation for forms.txt

  • docs/forms.txt

     
    1616
    1717We'll take a top-down approach to examining Django's form validation framework,
    1818because much of the time you won't need to use the lower-level APIs. Throughout
    19 this document, we'll be working with the following model, a "place" object::
     19this document, we'll be working with the following model, a "place" object
     20(that resides in the ``myproject/testapp`` directory if we ever need to
     21import it)::
    2022
    2123    PLACE_TYPES = (
    2224        (1, 'Bar'),
     
    5355the later modifies existing instances.  Both types of classes are automatically
    5456created when you define a new class::
    5557
    56     >>> from django.models.places import places
    57     >>> places.AddManipulator
    58     <class django.models.places.PlaceManipulatorAdd at 0x4c1540>
    59     >>> places.ChangeManipulator
    60     <class django.models.places.PlaceManipulatorChange at 0x4c1630>
     58    >>> from myproject.testapp import Place
     59    >>> Place.AddManipulator
     60    <class 'django.models.manipulators.AddManipulator'>
     61    >>> Place.ChangeManipulator
     62    <class 'django.models.manipulators.ChangeManipulator'>
    6163
    6264Using the ``AddManipulator``
    6365----------------------------
     
    6769
    6870    from django.shortcuts import render_to_response
    6971    from django.http import Http404, HttpResponse, HttpResponseRedirect
    70     from django.models.places import places
    7172    from django import forms
     73    from myproject.testapp import Place
    7274
    7375    def naive_create_place(request):
    7476        """A naive approach to creating places; don't actually use this!"""
    7577        # Create the AddManipulator.
    76         manipulator = places.AddManipulator()
     78        manipulator = Place.AddManipulator()
    7779
    7880        # Make a copy of the POSTed data so that do_html2python can
    7981        # modify it in place (request.POST is immutable).
     
    109111        """Simplistic place form view; don't actually use anything like this!"""
    110112        # Create a FormWrapper object that the template can use. Ignore
    111113        # the last two arguments to FormWrapper for now.
    112         form = forms.FormWrapper(places.AddManipulator(), {}, {})
    113         return render_to_response('places/naive_create_form.html', {'form': form})
     114        form = forms.FormWrapper(Place.AddManipulator(), {}, {})
     115        return render_to_response('testapp/naive_create_form.html', {'form': form})
    114116
    115117(This view, as well as all the following ones, has the same imports as in the
    116118first example above.)
     
    155157creation view that takes validation into account::
    156158
    157159    def create_place_with_validation(request):
    158         manipulator = places.AddManipulator()
     160        manipulator = Place.AddManipulator()
    159161        new_data = request.POST.copy()
    160162
    161163        # Check for validation errors
    162164        errors = manipulator.get_validation_errors(new_data)
    163165        if errors:
    164             return render_to_response('places/errors.html', {'errors': errors})
     166            return render_to_response('testapp/errors.html', {'errors': errors})
    165167        else:
    166             manipulator.do_html2python(request.POST)
    167             new_place = manipulator.save(request.POST)
     168            manipulator.do_html2python(new_data)
     169            new_place = manipulator.save(new_data)
    168170            return HttpResponse("Place created: %s" % new_place)
    169171
    170172In this new version, errors will be found -- ``manipulator.get_validation_errors``
     
    199201both be available on the same page, so errors with fields can be presented in
    200202context.
    201203
    202 .. admonition:: Philosophy::
     204.. admonition:: Philosophy:
    203205
    204206    Finally, for the HTTP purists in the audience (and the authorship), this
    205207    nicely matches the "true" meanings of HTTP GET and HTTP POST: GET fetches
     
    208210Below is the finished view::
    209211
    210212    def create_place(request):
    211         manipulator = places.AddManipulator()
     213        manipulator = Place.AddManipulator()
    212214
    213215        if request.POST:
    214216            # If data was POSTed, we're trying to create a new Place.
     
    233235
    234236        # Create the FormWrapper, template, context, response.
    235237        form = forms.FormWrapper(manipulator, new_data, errors)
    236         return render_to_response('places/create_form.html', {'form': form})
     238        return render_to_response('testapp/create_form.html', {'form': form})
    237239
    238240and here's the ``create_form`` template::
    239241
     
    300302        # Get the place in question from the database and create a
    301303        # ChangeManipulator at the same time.
    302304        try:
    303             manipulator = places.ChangeManipulator(place_id)
     305            manipulator = Place.ChangeManipulator(place_id)
    304306        except places.PlaceDoesNotExist:
    305307            raise Http404
    306308
     
    322324            new_data = place.__dict__
    323325
    324326        form = forms.FormWrapper(manipulator, new_data, errors)
    325         return render_to_response('places/edit_form.html', {'form': form, 'place': place})
     327        return render_to_response('testapp/edit_form.html', {'form': form, 'place': place})
    326328
    327329The only real differences are:
    328330
     
    409411One useful feature of manipulators is the automatic validation. Validation is
    410412done using a simple validation API: A validator is a callable that raises a
    411413``ValidationError`` if there's something wrong with the data.
    412 ``django.core.validators`` defines a host of validator functions, but defining
    413 your own couldn't be easier::
     414``django.core.validators`` defines a host of validator functions (see below),
     415but defining your own couldn't be easier::
    414416
    415417    from django.core import validators
    416418    from django import forms
     
    432434
    433435The arguments to a validator function take a little explanation.  ``field_data``
    434436is the value of the field in question, and ``all_data`` is a dictionary of all
    435 the data being validated.  Note that at the point validators are called all
    436 data will still be strings (as ``do_html2python`` hasn't been called yet).
     437the data being validated.
    437438
     439.. admonition:: Note::
     440
     441    At the point validators are called all data will still be
     442    strings (as ``do_html2python`` hasn't been called yet).
     443
    438444Also, because consistency in user interfaces is important, we strongly urge you
    439445to put punctuation at the end of your validation messages.
    440446
     447Ready-made Validators
     448---------------------
     449
     450Writing your own validator is not difficult, but there are some situations
     451that come up over and over again. Django comes with a number of validators
     452that can be used directly in your code. All of these functions and classes
     453reside in ``django/core/validators.py``.
     454
     455The following validators should all be self-explanatory. Each one provides a
     456check for the given property:
     457
     458    * isAlphaNumeric
     459    * isAlphaNumericURL
     460    * isSlug
     461    * isLowerCase
     462    * isUpperCase
     463    * isCommaSeparatedIntegerList
     464    * isCommaSeparatedEmailList
     465    * isValidIPAddress4
     466    * isNotEmpty
     467    * isOnlyDigits
     468    * isNotOnlyDigits
     469    * isInteger
     470    * isOnlyLetters
     471    * isValidANSIDate
     472    * isValidANSITime
     473    * isValidEmail
     474    * isValidImage
     475    * isValidImageURL
     476    * isValidPhone
     477    * isValidQuicktimeVideoURL
     478    * isValidURL
     479    * isValidHTML
     480    * isWellFormedXml
     481    * isWellFormedXmlFragment
     482    * isExistingURL
     483    * isValidUSState
     484    * hasNoProfanities
     485
     486There are also a group of validators that are slightly more flexible. For
     487these validators, you create a validator instance, passing in the parameters
     488described below. The returned object is a callable that can be used as a
     489validator.
     490
     491For example::
     492
     493    from django.core import validators
     494    from django import forms
     495
     496    power_validator = validators.IsAPowerOf(2)
     497
     498    class InstallationManipulator(forms.Manipulator)
     499        def __init__(self):
     500            self.fields = (
     501                ...
     502                forms.IntegerField(field_name = "size",i
     503                        validator_list=[power_validator])
     504            )
     505
     506Here, ``validators.IsAPowerOf(...)`` returned something that could be used as
     507a validator (in this case, a check that a number was a power of 2).
     508
     509Each of the standard validators that take parameters have an optional final
     510argument (``error_message``) that is the message returned when validation
     511fails. If no message is passed in, a default message is used.
     512
     513``AlwaysMatchesOtherField``
     514    Takes a field name and the current field is valid if and only if its value
     515    matches the contents of the other field.
     516
     517``ValidateIfOtherFieldEquals``
     518    Takes three parameters: ``other_field``, ``other_value`` and
     519    ``validator_list``, in that order. If ``other_field`` has a value of
     520    ``other_vaue``, then the validators in ``validator_list`` are all run
     521    against the current field.
     522
     523``RequiredIfOtherFieldNotGiven``
     524    Takes the name of the other field and this field is only required if the
     525    other field has no value.
     526
     527``RequiredIfOtherFieldsNotGiven``
     528    Similar to ``RequiredIfOtherFieldNotGiven``, except that it takes a list
     529    of field names and if any one of the supplied fields does not have a value
     530    provided, the field being validated is required.
     531
     532``RequiredIfOtherFieldEquals`` and ``RequiredIfOtherFieldDoesNotEqual``
     533    Each of these validator classes takes a field name and a value (in that
     534    order). If the given field does (or does not have, in the latter case) the
     535    given value, then the current field being validated is required.
     536
     537    Note that because validators are called before any ``do_html2python()``
     538    functions, the value being compared against is a string. So
     539    ``RequiredIfOtherFieldEquals('choice', '1')`` is correct, whilst
     540    ``RequiredIfOtherFieldEquals('choice', 1)`` will never result in the
     541    equality test succeeding.
     542
     543``IsLessThanOtherField``
     544    Takes a field name and validates that the current field being validated
     545    has a value that is less than (or equal to) the other field's value.
     546    Again, comparisons are done using strings, so be cautious about using
     547    this function to compare data that should be treated as another type. The
     548    string "123" is less than the string "2", for example. If you don't want
     549    string comparison here, you will need to write your own validator.
     550
     551``IsAPowerOf``
     552    Takes an integer argument and when called as a validator, checks that the
     553    field being validated is a power of the integer.
     554
     555``IsValidFloat``
     556    Takes a maximum number of digits and number of decimal places (in that
     557    order) and validates whether the field is a float with less than the
     558    maximum number of digits and decimal place.
     559
     560``MatchesRegularExpression``
     561    Takes a regular expression (a string) as a parameter and validates the
     562    field value against it.
     563
     564``AnyValidator``
     565    Takes a list of validators as a parameter. At validation time, if the
     566    field successfully validates against any one of the validators, it passes
     567    validation. The validators are tested in the order specified in the
     568    original list.
     569
     570``URLMimeTypeCheck``
     571    Used to validate URL fields. Takes a list of MIME types (such as
     572    ``text/plain``) at creation time. At validation time, it verifies that the
     573    field is indeed a URL and then tries to retrieve the content at the URL.
     574    Validation succeeds if the content could be retrieved and it has a content
     575    type from the list used to create the validator.
     576
     577``RelaxNGCompact``
     578    Used to validate an XML document against a Relax NG compact schema. Takes
     579    a file path to the location of the schema and an optional root element
     580    (which is wrapped around the XML fragment before validation, if supplied).
     581    At validation time, the XML fragment is validated against the schema using
     582    the executable specified in the ``JING_PATH`` setting (see the settings_
     583    document for more details).
     584
    441585.. _`generic views`: http://www.djangoproject.com/documentation/generic_views/
     586.. _`models API`: http://www.djangoproject.com/documentation/model_api/
     587.. _settings: http://www.djangoproject.com/documentation/settings/
Back to Top