From 24aa08f486d7fa7fbfc91b35dd41aadeb0c900da Mon Sep 17 00:00:00 2001 From: Gary Wilson Jr Date: Mon, 21 Jul 2008 16:38:54 +0000 Subject: Refs #7864 -- Updates to documentation for the oldforms/newforms switch. * Moved forms.txt to oldforms.txt * Moved newforms.txt to forms.txt * Updated links and most references to "newforms" (there are a few sections that need a more significant rewrite). git-svn-id: http://code.djangoproject.com/svn/django/trunk@8020 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- docs/forms.txt | 3025 +++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 2424 insertions(+), 601 deletions(-) (limited to 'docs/forms.txt') diff --git a/docs/forms.txt b/docs/forms.txt index 18d322a8eb..7781191e35 100644 --- a/docs/forms.txt +++ b/docs/forms.txt @@ -1,700 +1,2523 @@ -=============================== -Forms, fields, and manipulators -=============================== +================= +The forms library +================= -Forwards-compatibility note -=========================== +``django.forms`` is Django's fantastic new form-handling library. It's a +replacement for the old form/manipulator/validation framework, which has been +moved to ``django.oldforms``. This document explains how to use this new +library. -The legacy forms/manipulators system described in this document is going to be -replaced in the next Django release. If you're starting from scratch, we -strongly encourage you not to waste your time learning this. Instead, learn and -use the django.newforms system, which we have begun to document in the -`newforms documentation`_. +Migration plan +============== -If you have legacy form/manipulator code, read the "Migration plan" section in -that document to understand how we're making the switch. +``django.newforms`` is new in Django's 0.96 release, but, as it won't be new +forever, we plan to rename it to ``django.forms`` in the future. The current +``django.forms`` package will be available as ``django.oldforms`` until Django +1.0, when we plan to remove it for good. -.. _newforms documentation: ../newforms/ +That has direct repercussions on the forward compatibility of your code. Please +read the following migration plan and code accordingly: -Introduction -============ + * The old forms framework (the current ``django.forms``) has been copied to + ``django.oldforms``. Thus, you can start upgrading your code *now*, + rather than waiting for the future backwards-incompatible change, by + changing your import statements like this:: -Once you've got a chance to play with Django's admin interface, you'll probably -wonder if the fantastic form validation framework it uses is available to user -code. It is, and this document explains how the framework works. - -We'll take a top-down approach to examining Django's form validation framework, -because 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:: - - from django.db import models - - PLACE_TYPES = ( - (1, 'Bar'), - (2, 'Restaurant'), - (3, 'Movie Theater'), - (4, 'Secret Hideout'), - ) - - class Place(models.Model): - name = models.CharField(max_length=100) - address = models.CharField(max_length=100, blank=True) - city = models.CharField(max_length=50, blank=True) - state = models.USStateField() - zip_code = models.CharField(max_length=5, blank=True) - place_type = models.IntegerField(choices=PLACE_TYPES) - - class Admin: - pass - - def __unicode__(self): - return self.name - -Defining the above class is enough to create an admin interface to a ``Place``, -but what if you want to allow public users to submit places? - -Automatic Manipulators -====================== - -The highest-level interface for object creation and modification is the -**automatic Manipulator** framework. An automatic manipulator is a utility -class tied to a given model that "knows" how to create or modify instances of -that model and how to validate data for the object. Automatic Manipulators come -in two flavors: ``AddManipulators`` and ``ChangeManipulators``. Functionally -they are quite similar, but the former knows how to create new instances of the -model, while the latter modifies existing instances. Both types of classes are -automatically created when you define a new class:: - - >>> from mysite.myapp.models import Place - >>> Place.AddManipulator - - >>> Place.ChangeManipulator - - -Using the ``AddManipulator`` ----------------------------- + from django import forms # old + from django import oldforms as forms # new -We'll start with the ``AddManipulator``. Here's a very simple view that takes -POSTed data from the browser and creates a new ``Place`` object:: + * In the next Django release (0.97), we will move the current + ``django.newforms`` to ``django.forms``. This will be a + backwards-incompatible change, and anybody who is still using the old + version of ``django.forms`` at that time will need to change their import + statements, as described in the previous bullet. - from django.shortcuts import render_to_response - from django.http import Http404, HttpResponse, HttpResponseRedirect - from django import forms - from mysite.myapp.models import Place + * We will remove ``django.oldforms`` in the release *after* the next Django + release -- either 0.98 or 1.0, whichever comes first. - def naive_create_place(request): - """A naive approach to creating places; don't actually use this!""" - # Create the AddManipulator. - manipulator = Place.AddManipulator() +With this in mind, we recommend you use the following import statement when +using ``django.newforms``:: - # Make a copy of the POSTed data so that do_html2python can - # modify it in place (request.POST is immutable). - new_data = request.POST.copy() + from django import newforms as forms - # Convert the request data (which will all be strings) into the - # appropriate Python types for those fields. - manipulator.do_html2python(new_data) +This way, your code can refer to the ``forms`` module, and when +``django.newforms`` is renamed to ``django.forms``, you'll only have to change +your ``import`` statements. - # Save the new object. - new_place = manipulator.save(new_data) +If you prefer "``import *``" syntax, you can do the following:: - # It worked! - return HttpResponse("Place created: %s" % new_place) + from django.newforms import * -The ``naive_create_place`` example works, but as you probably can tell, this -view has a number of problems: +This will import all fields, widgets, form classes and other various utilities +into your local namespace. Some people find this convenient; others find it +too messy. The choice is yours. - * 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 - because that field is required. Ugly. +Overview +======== - * Even if you *do* perform validation, there's still no way to give that - information to the user in any sort of useful way. +As with the ``django.oldforms`` ("manipulators") system before it, +``django.forms`` is intended to handle HTML form display, data processing +(validation) and redisplay. It's what you use if you want to perform +server-side validation for an HTML form. - * You'll have to separately create a form (and view) that submits to this - page, which is a pain and is redundant. +For example, if your Web site has a contact form that visitors can use to +send you e-mail, you'd use this library to implement the display of the HTML +form fields, along with the form validation. Any time you need to use an HTML +``
``, you can use this library. -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:: +The library deals with these concepts: - def naive_create_place_form(request): - """Simplistic place form view; don't actually use anything like this!""" - # Create a FormWrapper object that the template can use. Ignore - # the last two arguments to FormWrapper for now. - form = forms.FormWrapper(Place.AddManipulator(), {}, {}) - return render_to_response('places/naive_create_form.html', {'form': form}) + * **Widget** -- A class that corresponds to an HTML form widget, e.g. + ```` or ```` + ``CheckboxInput`` ``
-Also, because consistency in user interfaces is important, we strongly urge you -to put punctuation at the end of your validation messages. +However the above can be slightly shortcutted and let the formset itself deal +with the management form:: -When are validators called? ---------------------------- +
+ + {{ formset }} +
+
-After a form has been submitted, Django validates each field in turn. First, -if the field is required, Django checks that it is present and non-empty. Then, -if that test passes *and the form submission contained data* for that field, all -the validators for that field are called in turn. The emphasized portion in the -last sentence is important: if a form field is not submitted (because it -contains no data -- which is normal HTML behavior), the validators are not -run against the field. - -This feature is particularly important for models using -``models.BooleanField`` or custom manipulators using things like -``forms.CheckBoxField``. If the checkbox is not selected, it will not -contribute to the form submission. - -If you would like your validator to run *always*, regardless of whether its -attached field contains any data, set the ``always_test`` attribute on the -validator function. For example:: - - def my_custom_validator(field_data, all_data): - # ... - my_custom_validator.always_test = True - -This validator will always be executed for any field it is attached to. - -Ready-made validators ---------------------- - -Writing your own validator is not difficult, but there are some situations -that come up over and over again. Django comes with a number of validators -that can be used directly in your code. All of these functions and classes -reside in ``django/core/validators.py``. - -The following validators should all be self-explanatory. Each one provides a -check for the given property: - - * isAlphaNumeric - * isAlphaNumericURL - * isSlug - * isLowerCase - * isUpperCase - * isCommaSeparatedIntegerList - * isCommaSeparatedEmailList - * isValidIPAddress4 - * isNotEmpty - * isOnlyDigits - * isNotOnlyDigits - * isInteger - * isOnlyLetters - * isValidANSIDate - * isValidANSITime - * isValidEmail - * isValidFloat - * isValidImage - * isValidImageURL - * isValidPhone - * isValidQuicktimeVideoURL - * isValidURL - * isValidHTML - * isWellFormedXml - * isWellFormedXmlFragment - * isExistingURL - * isValidUSState - * hasNoProfanities - -There are also a group of validators that are slightly more flexible. For -these validators, you create a validator instance, passing in the parameters -described below. The returned object is a callable that can be used as a -validator. +The above ends up calling the ``as_table`` method on the formset class. -For example:: +More coming soon +================ - from django.core import validators - from django import forms +That's all the documentation for now. For more, see the file +http://code.djangoproject.com/browser/django/trunk/tests/regressiontests/forms +-- the unit tests for ``django.forms``. This can give you a good idea of +what's possible. (Each submodule there contains separate tests.) - power_validator = validators.IsAPowerOf(2) - - class InstallationManipulator(forms.Manipulator) - def __init__(self): - self.fields = ( - ... - forms.IntegerField(field_name = "size", validator_list=[power_validator]) - ) - -Here, ``validators.IsAPowerOf(...)`` returned something that could be used as -a validator (in this case, a check that a number was a power of 2). - -Each of the standard validators that take parameters have an optional final -argument (``error_message``) that is the message returned when validation -fails. If no message is passed in, a default message is used. - -``AlwaysMatchesOtherField`` - Takes a field name and the current field is valid if and only if its value - matches the contents of the other field. - -``ValidateIfOtherFieldEquals`` - Takes three parameters: ``other_field``, ``other_value`` and - ``validator_list``, in that order. If ``other_field`` has a value of - ``other_value``, then the validators in ``validator_list`` are all run - against the current field. - -``RequiredIfOtherFieldGiven`` - Takes a field name of the current field is only required if the other - field has a value. - -``RequiredIfOtherFieldsGiven`` - Similar to ``RequiredIfOtherFieldGiven``, except that it takes a list of - field names and if any one of the supplied fields has a value provided, - the current field being validated is required. - -``RequiredIfOtherFieldNotGiven`` - Takes the name of the other field and this field is only required if the - other field has no value. - -``RequiredIfOtherFieldEquals`` and ``RequiredIfOtherFieldDoesNotEqual`` - Each of these validator classes takes a field name and a value (in that - order). If the given field does (or does not have, in the latter case) the - given value, then the current field being validated is required. - - An optional ``other_label`` argument can be passed which, if given, is used - in error messages instead of the value. This allows more user friendly error - messages if the value itself is not descriptive enough. - - Note that because validators are called before any ``do_html2python()`` - functions, the value being compared against is a string. So - ``RequiredIfOtherFieldEquals('choice', '1')`` is correct, whilst - ``RequiredIfOtherFieldEquals('choice', 1)`` will never result in the - equality test succeeding. - -``IsLessThanOtherField`` - Takes a field name and validates that the current field being validated - has a value that is less than (or equal to) the other field's value. - Again, comparisons are done using strings, so be cautious about using - this function to compare data that should be treated as another type. The - string "123" is less than the string "2", for example. If you don't want - string comparison here, you will need to write your own validator. - -``NumberIsInRange`` - Takes two boundary numbers, ``lower`` and ``upper``, and checks that the - field is greater than ``lower`` (if given) and less than ``upper`` (if - given). - - Both checks are inclusive. That is, ``NumberIsInRange(10, 20)`` will allow - values of both 10 and 20. This validator only checks numeric values - (e.g., float and integer values). - -``IsAPowerOf`` - Takes an integer argument and when called as a validator, checks that the - field being validated is a power of the integer. - -``IsValidDecimal`` - Takes a maximum number of digits and number of decimal places (in that - order) and validates whether the field is a decimal with no more than the - maximum number of digits and decimal places. - -``MatchesRegularExpression`` - Takes a regular expression (a string) as a parameter and validates the - field value against it. - -``AnyValidator`` - Takes a list of validators as a parameter. At validation time, if the - field successfully validates against any one of the validators, it passes - validation. The validators are tested in the order specified in the - original list. - -``URLMimeTypeCheck`` - Used to validate URL fields. Takes a list of MIME types (such as - ``text/plain``) at creation time. At validation time, it verifies that the - field is indeed a URL and then tries to retrieve the content at the URL. - Validation succeeds if the content could be retrieved and it has a content - type from the list used to create the validator. - -``RelaxNGCompact`` - Used to validate an XML document against a Relax NG compact schema. Takes - a file path to the location of the schema and an optional root element - (which is wrapped around the XML fragment before validation, if supplied). - At validation time, the XML fragment is validated against the schema using - the executable specified in the ``JING_PATH`` setting (see the settings_ - document for more details). - -.. _`generic views`: ../generic_views/ -.. _`models API`: ../model-api/ -.. _settings: ../settings/ +If you're really itching to learn and use this library, please be patient. +We're working hard on finishing both the code and documentation. -- cgit v1.3