Ticket #1703: forms.txt.diff
File forms.txt.diff, 11.9 KB (added by , 19 years ago) |
---|
-
docs/forms.txt
16 16 17 17 We'll take a top-down approach to examining Django's form validation framework, 18 18 because 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:: 19 this 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 21 import it):: 20 22 21 23 PLACE_TYPES = ( 22 24 (1, 'Bar'), … … 53 55 the later modifies existing instances. Both types of classes are automatically 54 56 created when you define a new class:: 55 57 56 >>> from django.models.places import places57 >>> places.AddManipulator58 <class django.models.places.PlaceManipulatorAdd at 0x4c1540>59 >>> places.ChangeManipulator60 <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'> 61 63 62 64 Using the ``AddManipulator`` 63 65 ---------------------------- … … 67 69 68 70 from django.shortcuts import render_to_response 69 71 from django.http import Http404, HttpResponse, HttpResponseRedirect 70 from django.models.places import places71 72 from django import forms 73 from myproject.testapp import Place 72 74 73 75 def naive_create_place(request): 74 76 """A naive approach to creating places; don't actually use this!""" 75 77 # Create the AddManipulator. 76 manipulator = places.AddManipulator()78 manipulator = Place.AddManipulator() 77 79 78 80 # Make a copy of the POSTed data so that do_html2python can 79 81 # modify it in place (request.POST is immutable). … … 109 111 """Simplistic place form view; don't actually use anything like this!""" 110 112 # Create a FormWrapper object that the template can use. Ignore 111 113 # 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}) 114 116 115 117 (This view, as well as all the following ones, has the same imports as in the 116 118 first example above.) … … 155 157 creation view that takes validation into account:: 156 158 157 159 def create_place_with_validation(request): 158 manipulator = places.AddManipulator()160 manipulator = Place.AddManipulator() 159 161 new_data = request.POST.copy() 160 162 161 163 # Check for validation errors 162 164 errors = manipulator.get_validation_errors(new_data) 163 165 if errors: 164 return render_to_response(' places/errors.html', {'errors': errors})166 return render_to_response('testapp/errors.html', {'errors': errors}) 165 167 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) 168 170 return HttpResponse("Place created: %s" % new_place) 169 171 170 172 In this new version, errors will be found -- ``manipulator.get_validation_errors`` … … 199 201 both be available on the same page, so errors with fields can be presented in 200 202 context. 201 203 202 .. admonition:: Philosophy: :204 .. admonition:: Philosophy: 203 205 204 206 Finally, for the HTTP purists in the audience (and the authorship), this 205 207 nicely matches the "true" meanings of HTTP GET and HTTP POST: GET fetches … … 208 210 Below is the finished view:: 209 211 210 212 def create_place(request): 211 manipulator = places.AddManipulator()213 manipulator = Place.AddManipulator() 212 214 213 215 if request.POST: 214 216 # If data was POSTed, we're trying to create a new Place. … … 233 235 234 236 # Create the FormWrapper, template, context, response. 235 237 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}) 237 239 238 240 and here's the ``create_form`` template:: 239 241 … … 300 302 # Get the place in question from the database and create a 301 303 # ChangeManipulator at the same time. 302 304 try: 303 manipulator = places.ChangeManipulator(place_id)305 manipulator = Place.ChangeManipulator(place_id) 304 306 except places.PlaceDoesNotExist: 305 307 raise Http404 306 308 … … 322 324 new_data = place.__dict__ 323 325 324 326 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}) 326 328 327 329 The only real differences are: 328 330 … … 409 411 One useful feature of manipulators is the automatic validation. Validation is 410 412 done using a simple validation API: A validator is a callable that raises a 411 413 ``ValidationError`` if there's something wrong with the data. 412 ``django.core.validators`` defines a host of validator functions , but defining413 your own couldn't be easier::414 ``django.core.validators`` defines a host of validator functions (see below), 415 but defining your own couldn't be easier:: 414 416 415 417 from django.core import validators 416 418 from django import forms … … 432 434 433 435 The arguments to a validator function take a little explanation. ``field_data`` 434 436 is 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). 437 the data being validated. 437 438 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 438 444 Also, because consistency in user interfaces is important, we strongly urge you 439 445 to put punctuation at the end of your validation messages. 440 446 447 Ready-made Validators 448 --------------------- 449 450 Writing your own validator is not difficult, but there are some situations 451 that come up over and over again. Django comes with a number of validators 452 that can be used directly in your code. All of these functions and classes 453 reside in ``django/core/validators.py``. 454 455 The following validators should all be self-explanatory. Each one provides a 456 check 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 486 There are also a group of validators that are slightly more flexible. For 487 these validators, you create a validator instance, passing in the parameters 488 described below. The returned object is a callable that can be used as a 489 validator. 490 491 For 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 506 Here, ``validators.IsAPowerOf(...)`` returned something that could be used as 507 a validator (in this case, a check that a number was a power of 2). 508 509 Each of the standard validators that take parameters have an optional final 510 argument (``error_message``) that is the message returned when validation 511 fails. 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 441 585 .. _`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/