diff options
| author | Joseph Kocherhans <joseph@jkocherhans.com> | 2007-05-15 03:37:41 +0000 |
|---|---|---|
| committer | Joseph Kocherhans <joseph@jkocherhans.com> | 2007-05-15 03:37:41 +0000 |
| commit | 433659139596a75eab03940ea2029970de6ee287 (patch) | |
| tree | b4ee37f43df2d7a560a403a9d391c51702946772 /tests | |
| parent | 415e84ad53e0d0d8f7df87784c1893489bdbe0b8 (diff) | |
newforms-admin: Merged to [5243]. There are 3 failing tests in regressiontests.serializers_regress.tests.SerializerTests, but they fail in trunk also.
git-svn-id: http://code.djangoproject.com/svn/django/branches/newforms-admin@5244 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/modeltests/model_forms/models.py | 44 | ||||
| -rw-r--r-- | tests/regressiontests/forms/formsets.py | 32 | ||||
| -rw-r--r-- | tests/regressiontests/forms/regressions.py | 14 | ||||
| -rw-r--r-- | tests/regressiontests/forms/tests.py | 158 | ||||
| -rw-r--r-- | tests/regressiontests/serializers_regress/models.py | 10 | ||||
| -rw-r--r-- | tests/regressiontests/serializers_regress/tests.py | 6 | ||||
| -rw-r--r-- | tests/regressiontests/test_client_regress/models.py | 28 |
7 files changed, 232 insertions, 60 deletions
diff --git a/tests/modeltests/model_forms/models.py b/tests/modeltests/model_forms/models.py index a23529b566..6ffd4d1bce 100644 --- a/tests/modeltests/model_forms/models.py +++ b/tests/modeltests/model_forms/models.py @@ -18,7 +18,7 @@ other Form, with one additional method: save(). The save() method updates the model instance. It also takes a commit=True parameter. The function django.newforms.save_instance() takes a bound form instance and a -model instance and saves the form's clean_data into the instance. It also takes +model instance and saves the form's cleaned_data into the instance. It also takes a commit=True parameter. """ @@ -94,7 +94,7 @@ __test__ = {'API_TESTS': """ >>> f = CategoryForm({'name': 'Entertainment', 'url': 'entertainment'}) >>> f.is_valid() True ->>> f.clean_data +>>> f.cleaned_data {'url': u'entertainment', 'name': u'Entertainment'} >>> obj = f.save() >>> obj @@ -105,7 +105,7 @@ True >>> f = CategoryForm({'name': "It's a test", 'url': 'test'}) >>> f.is_valid() True ->>> f.clean_data +>>> f.cleaned_data {'url': u'test', 'name': u"It's a test"} >>> obj = f.save() >>> obj @@ -119,7 +119,7 @@ save() on the resulting model instance. >>> f = CategoryForm({'name': 'Third test', 'url': 'third'}) >>> f.is_valid() True ->>> f.clean_data +>>> f.cleaned_data {'url': u'third', 'name': u'Third test'} >>> obj = f.save(commit=False) >>> obj @@ -134,10 +134,10 @@ If you call save() with invalid data, you'll get a ValueError. >>> f = CategoryForm({'name': '', 'url': 'foo'}) >>> f.errors {'name': [u'This field is required.']} ->>> f.clean_data +>>> f.cleaned_data Traceback (most recent call last): ... -AttributeError: 'CategoryForm' object has no attribute 'clean_data' +AttributeError: 'CategoryForm' object has no attribute 'cleaned_data' >>> f.save() Traceback (most recent call last): ... @@ -179,6 +179,18 @@ fields with the 'choices' attribute are represented by a ChoiceField. <option value="3">Third test</option> </select><br /> Hold down "Control", or "Command" on a Mac, to select more than one.</td></tr> +You can restrict a form to a subset of the complete list of fields +by providing a 'fields' argument. If you try to save a +model created with such a form, you need to ensure that the fields +that are _not_ on the form have default values, or are allowed to have +a value of None. If a field isn't specified on a form, the object created +from the form can't provide a value for that field! +>>> PartialArticleForm = form_for_model(Article, fields=('headline','pub_date')) +>>> f = PartialArticleForm(auto_id=False) +>>> print f +<tr><th>Headline:</th><td><input type="text" name="headline" maxlength="50" /></td></tr> +<tr><th>Pub date:</th><td><input type="text" name="pub_date" /></td></tr> + You can pass a custom Form class to form_for_model. Make sure it's a subclass of BaseForm, not Form. >>> class CustomForm(BaseForm): @@ -224,7 +236,23 @@ current values are inserted as 'initial' data in each Field. <option value="2">It's a test</option> <option value="3">Third test</option> </select> Hold down "Control", or "Command" on a Mac, to select more than one.</li> ->>> f = TestArticleForm({'headline': u'New headline', 'pub_date': u'1988-01-04', 'writer': u'1', 'article': 'Hello.'}) +>>> f = TestArticleForm({'headline': u'Test headline', 'pub_date': u'1984-02-06', 'writer': u'1', 'article': 'Hello.'}) +>>> f.is_valid() +True +>>> test_art = f.save() +>>> test_art.id +1 +>>> test_art = Article.objects.get(id=1) +>>> test_art.headline +'Test headline' + +You can create a form over a subset of the available fields +by specifying a 'fields' argument to form_for_instance. +>>> PartialArticleForm = form_for_instance(art, fields=('headline','pub_date')) +>>> f = PartialArticleForm({'headline': u'New headline', 'pub_date': u'1988-01-04'}, auto_id=False) +>>> print f.as_ul() +<li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" /></li> +<li>Pub date: <input type="text" name="pub_date" value="1988-01-04" /></li> >>> f.is_valid() True >>> new_art = f.save() @@ -496,6 +524,6 @@ ValidationError: [u'Select a valid choice. 10 is not one of the available choice >>> f = PhoneNumberForm({'phone': '(312) 555-1212', 'description': 'Assistance'}) >>> f.is_valid() True ->>> f.clean_data +>>> f.cleaned_data {'phone': u'312-555-1212', 'description': u'Assistance'} """} diff --git a/tests/regressiontests/forms/formsets.py b/tests/regressiontests/forms/formsets.py index 8d0e3b8d7c..96aa86a9b2 100644 --- a/tests/regressiontests/forms/formsets.py +++ b/tests/regressiontests/forms/formsets.py @@ -38,14 +38,14 @@ the COUNT field appropriately. ... } We treat FormSet pretty much like we would treat a normal Form. FormSet has an -is_valid method, and a clean_data or errors attribute depending on whether all -the forms passed validation. However, unlike a Form instance, clean_data and +is_valid method, and a cleaned_data or errors attribute depending on whether all +the forms passed validation. However, unlike a Form instance, cleaned_data and errors will be a list of dicts rather than just a single dict. >>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices') >>> formset.is_valid() True ->>> formset.clean_data +>>> formset.cleaned_data [{'votes': 100, 'choice': u'Calexico'}] @@ -64,12 +64,12 @@ False >>> formset.errors [{'votes': [u'This field is required.']}] -Like a Form instance, clean_data won't exist if the formset wasn't validated. +Like a Form instance, cleaned_data won't exist if the formset wasn't validated. ->>> formset.clean_data +>>> formset.cleaned_data Traceback (most recent call last): ... -AttributeError: 'ChoiceFormSet' object has no attribute 'clean_data' +AttributeError: 'ChoiceFormSet' object has no attribute 'cleaned_data' We can also prefill a FormSet with existing data by providing an ``initial`` @@ -99,7 +99,7 @@ Let's simulate what would happen if we submitted this form. >>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices') >>> formset.is_valid() True ->>> formset.clean_data +>>> formset.cleaned_data [{'votes': 100, 'choice': u'Calexico'}] But the second form was blank! Shouldn't we get some errors? No. If we display @@ -176,7 +176,7 @@ number of forms to be completed. >>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices') >>> formset.is_valid() True ->>> formset.clean_data +>>> formset.cleaned_data [] @@ -195,7 +195,7 @@ We can just fill out one of the forms. >>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices') >>> formset.is_valid() True ->>> formset.clean_data +>>> formset.cleaned_data [{'votes': 100, 'choice': u'Calexico'}] @@ -262,7 +262,7 @@ False We can easily add deletion ability to a FormSet with an agrument to formset_for_form. This will add a boolean field to each form instance. When that boolean field is True, the cleaned data will be in formset.deleted_data -rather than formset.clean_data +rather than formset.cleaned_data >>> ChoiceFormSet = formset_for_form(Choice, deletable=True) @@ -299,7 +299,7 @@ To delete something, we just need to set that form's special delete field to >>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices') >>> formset.is_valid() True ->>> formset.clean_data +>>> formset.cleaned_data [{'votes': 100, 'DELETE': False, 'choice': u'Calexico'}] >>> formset.deleted_data [{'votes': 900, 'DELETE': True, 'choice': u'Fergie'}] @@ -308,7 +308,7 @@ True We can also add ordering ability to a FormSet with an agrument to formset_for_form. This will add a integer field to each form instance. When -form validation succeeds, formset.clean_data will have the data in the correct +form validation succeeds, formset.cleaned_data will have the data in the correct order specified by the ordering fields. If a number is duplicated in the set of ordering fields, for instance form 0 and form 3 are both marked as 1, then the form index used as a secondary ordering criteria. In order to put @@ -346,8 +346,8 @@ something at the front of the list, you'd need to set it's order to 0. >>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices') >>> formset.is_valid() True ->>> for clean_data in formset.clean_data: -... print clean_data +>>> for cleaned_data in formset.cleaned_data: +... print cleaned_data {'votes': 500, 'ORDER': 0, 'choice': u'The Decemberists'} {'votes': 100, 'ORDER': 1, 'choice': u'Calexico'} {'votes': 900, 'ORDER': 2, 'choice': u'Fergie'} @@ -408,8 +408,8 @@ Let's delete Fergie, and put The Decemberists ahead of Calexico. >>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices') >>> formset.is_valid() True ->>> for clean_data in formset.clean_data: -... print clean_data +>>> for cleaned_data in formset.cleaned_data: +... print cleaned_data {'votes': 500, 'DELETE': False, 'ORDER': 0, 'choice': u'The Decemberists'} {'votes': 100, 'DELETE': False, 'ORDER': 1, 'choice': u'Calexico'} >>> formset.deleted_data diff --git a/tests/regressiontests/forms/regressions.py b/tests/regressiontests/forms/regressions.py index 5daabc03af..5fe057b5d8 100644 --- a/tests/regressiontests/forms/regressions.py +++ b/tests/regressiontests/forms/regressions.py @@ -34,4 +34,18 @@ Unicode decoding problems... >>> f = SomeForm() >>> f.as_p() u'<p><label for="id_somechoice_0">Somechoice:</label> <ul>\n<li><label><input type="radio" id="id_somechoice_0" value="0" name="somechoice" /> En tied\xe4</label></li>\n<li><label><input type="radio" id="id_somechoice_1" value="1" name="somechoice" /> Mies</label></li>\n<li><label><input type="radio" id="id_somechoice_2" value="2" name="somechoice" /> Nainen</label></li>\n</ul></p>' + +####################### +# Miscellaneous Tests # +####################### + +There once was a problem with Form fields called "data". Let's make sure that +doesn't come back. +>>> class DataForm(Form): +... data = CharField(max_length=10) +>>> f = DataForm({'data': 'xyzzy'}) +>>> f.is_valid() +True +>>> f.cleaned_data +{'data': u'xyzzy'} """ diff --git a/tests/regressiontests/forms/tests.py b/tests/regressiontests/forms/tests.py index f15034ec66..943cddd1d7 100644 --- a/tests/regressiontests/forms/tests.py +++ b/tests/regressiontests/forms/tests.py @@ -1775,7 +1775,7 @@ True u'' >>> p.errors.as_text() u'' ->>> p.clean_data +>>> p.cleaned_data {'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)} >>> print p['first_name'] <input type="text" name="first_name" value="John" id="id_first_name" /> @@ -1811,10 +1811,10 @@ True {'first_name': [u'This field is required.'], 'last_name': [u'This field is required.'], 'birthday': [u'This field is required.']} >>> p.is_valid() False ->>> p.clean_data +>>> p.cleaned_data Traceback (most recent call last): ... -AttributeError: 'Person' object has no attribute 'clean_data' +AttributeError: 'Person' object has no attribute 'cleaned_data' >>> print p <tr><th><label for="id_first_name">First name:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="first_name" id="id_first_name" /></td></tr> <tr><th><label for="id_last_name">Last name:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="last_name" id="id_last_name" /></td></tr> @@ -1845,10 +1845,10 @@ False {} >>> p.is_valid() False ->>> p.clean_data +>>> p.cleaned_data Traceback (most recent call last): ... -AttributeError: 'Person' object has no attribute 'clean_data' +AttributeError: 'Person' object has no attribute 'cleaned_data' >>> print p <tr><th><label for="id_first_name">First name:</label></th><td><input type="text" name="first_name" id="id_first_name" /></td></tr> <tr><th><label for="id_last_name">Last name:</label></th><td><input type="text" name="last_name" id="id_last_name" /></td></tr> @@ -1887,10 +1887,10 @@ u'<ul class="errorlist"><li>first_name<ul class="errorlist"><li>This field is re * This field is required. * birthday * This field is required. ->>> p.clean_data +>>> p.cleaned_data Traceback (most recent call last): ... -AttributeError: 'Person' object has no attribute 'clean_data' +AttributeError: 'Person' object has no attribute 'cleaned_data' >>> p['first_name'].errors [u'This field is required.'] >>> p['first_name'].errors.as_ul() @@ -1906,17 +1906,45 @@ u'* This field is required.' >>> print p['birthday'] <input type="text" name="birthday" id="id_birthday" /> -clean_data will always *only* contain a key for fields defined in the +cleaned_data will always *only* contain a key for fields defined in the Form, even if you pass extra data when you define the Form. In this example, we pass a bunch of extra fields to the form constructor, -but clean_data contains only the form's fields. +but cleaned_data contains only the form's fields. >>> data = {'first_name': u'John', 'last_name': u'Lennon', 'birthday': u'1940-10-9', 'extra1': 'hello', 'extra2': 'hello'} >>> p = Person(data) >>> p.is_valid() True ->>> p.clean_data +>>> p.cleaned_data {'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)} +cleaned_data will include a key and value for *all* fields defined in the Form, +even if the Form's data didn't include a value for fields that are not +required. In this example, the data dictionary doesn't include a value for the +"nick_name" field, but cleaned_data includes it. For CharFields, it's set to the +empty string. +>>> class OptionalPersonForm(Form): +... first_name = CharField() +... last_name = CharField() +... nick_name = CharField(required=False) +>>> data = {'first_name': u'John', 'last_name': u'Lennon'} +>>> f = OptionalPersonForm(data) +>>> f.is_valid() +True +>>> f.cleaned_data +{'nick_name': u'', 'first_name': u'John', 'last_name': u'Lennon'} + +For DateFields, it's set to None. +>>> class OptionalPersonForm(Form): +... first_name = CharField() +... last_name = CharField() +... birth_date = DateField(required=False) +>>> data = {'first_name': u'John', 'last_name': u'Lennon'} +>>> f = OptionalPersonForm(data) +>>> f.is_valid() +True +>>> f.cleaned_data +{'birth_date': None, 'first_name': u'John', 'last_name': u'Lennon'} + "auto_id" tells the Form to add an "id" attribute to each form element. If it's a string that contains '%s', Django will use that as a format string into which the field's name will be inserted. It will also put a <label> around @@ -2265,19 +2293,19 @@ returns a list of input. >>> f = SongForm({'name': 'Yesterday', 'composers': ['J']}, auto_id=False) >>> f.errors {} ->>> f.clean_data +>>> f.cleaned_data {'composers': [u'J'], 'name': u'Yesterday'} >>> f = SongForm({'name': 'Yesterday', 'composers': ['J', 'P']}, auto_id=False) >>> f.errors {} ->>> f.clean_data +>>> f.cleaned_data {'composers': [u'J', u'P'], 'name': u'Yesterday'} Validation errors are HTML-escaped when output as HTML. >>> class EscapingForm(Form): ... special_name = CharField() ... def clean_special_name(self): -... raise ValidationError("Something's wrong with '%s'" % self.clean_data['special_name']) +... raise ValidationError("Something's wrong with '%s'" % self.cleaned_data['special_name']) >>> f = EscapingForm({'special_name': "Nothing to escape"}, auto_id=False) >>> print f @@ -2292,7 +2320,7 @@ There are a couple of ways to do multiple-field validation. If you want the validation message to be associated with a particular field, implement the clean_XXX() method on the Form, where XXX is the field name. As in Field.clean(), the clean_XXX() method should return the cleaned value. In the -clean_XXX() method, you have access to self.clean_data, which is a dictionary +clean_XXX() method, you have access to self.cleaned_data, which is a dictionary of all the data that has been cleaned *so far*, in order by the fields, including the current field (e.g., the field XXX if you're in clean_XXX()). >>> class UserRegistration(Form): @@ -2300,9 +2328,9 @@ including the current field (e.g., the field XXX if you're in clean_XXX()). ... password1 = CharField(widget=PasswordInput) ... password2 = CharField(widget=PasswordInput) ... def clean_password2(self): -... if self.clean_data.get('password1') and self.clean_data.get('password2') and self.clean_data['password1'] != self.clean_data['password2']: +... if self.cleaned_data.get('password1') and self.cleaned_data.get('password2') and self.cleaned_data['password1'] != self.cleaned_data['password2']: ... raise ValidationError(u'Please make sure your passwords match.') -... return self.clean_data['password2'] +... return self.cleaned_data['password2'] >>> f = UserRegistration(auto_id=False) >>> f.errors {} @@ -2315,14 +2343,14 @@ including the current field (e.g., the field XXX if you're in clean_XXX()). >>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'}, auto_id=False) >>> f.errors {} ->>> f.clean_data +>>> f.cleaned_data {'username': u'adrian', 'password1': u'foo', 'password2': u'foo'} Another way of doing multiple-field validation is by implementing the Form's clean() method. If you do this, any ValidationError raised by that method will not be associated with a particular field; it will have a special-case association with the field named '__all__'. -Note that in Form.clean(), you have access to self.clean_data, a dictionary of +Note that in Form.clean(), you have access to self.cleaned_data, a dictionary of all the fields/values that have *not* raised a ValidationError. Also note Form.clean() is required to return a dictionary of all clean data. >>> class UserRegistration(Form): @@ -2330,9 +2358,9 @@ Form.clean() is required to return a dictionary of all clean data. ... password1 = CharField(widget=PasswordInput) ... password2 = CharField(widget=PasswordInput) ... def clean(self): -... if self.clean_data.get('password1') and self.clean_data.get('password2') and self.clean_data['password1'] != self.clean_data['password2']: +... if self.cleaned_data.get('password1') and self.cleaned_data.get('password2') and self.cleaned_data['password1'] != self.cleaned_data['password2']: ... raise ValidationError(u'Please make sure your passwords match.') -... return self.clean_data +... return self.cleaned_data >>> f = UserRegistration(auto_id=False) >>> f.errors {} @@ -2359,7 +2387,7 @@ Form.clean() is required to return a dictionary of all clean data. >>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'}, auto_id=False) >>> f.errors {} ->>> f.clean_data +>>> f.cleaned_data {'username': u'adrian', 'password1': u'foo', 'password2': u'foo'} # Dynamic construction ######################################################## @@ -2753,6 +2781,64 @@ then the latter will get precedence. <li>Username: <input type="text" name="username" value="babik" maxlength="10" /></li> <li>Password: <input type="password" name="password" /></li> +# Callable initial data ######################################################## + +The previous technique dealt with raw values as initial data, but it's also +possible to specify callable data. + +>>> class UserRegistration(Form): +... username = CharField(max_length=10) +... password = CharField(widget=PasswordInput) + +We need to define functions that get called later. +>>> def initial_django(): +... return 'django' +>>> def initial_stephane(): +... return 'stephane' + +Here, we're not submitting any data, so the initial value will be displayed. +>>> p = UserRegistration(initial={'username': initial_django}, auto_id=False) +>>> print p.as_ul() +<li>Username: <input type="text" name="username" value="django" maxlength="10" /></li> +<li>Password: <input type="password" name="password" /></li> + +The 'initial' parameter is meaningless if you pass data. +>>> p = UserRegistration({}, initial={'username': initial_django}, auto_id=False) +>>> print p.as_ul() +<li><ul class="errorlist"><li>This field is required.</li></ul>Username: <input type="text" name="username" maxlength="10" /></li> +<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li> +>>> p = UserRegistration({'username': u''}, initial={'username': initial_django}, auto_id=False) +>>> print p.as_ul() +<li><ul class="errorlist"><li>This field is required.</li></ul>Username: <input type="text" name="username" maxlength="10" /></li> +<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li> +>>> p = UserRegistration({'username': u'foo'}, initial={'username': initial_django}, auto_id=False) +>>> print p.as_ul() +<li>Username: <input type="text" name="username" value="foo" maxlength="10" /></li> +<li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li> + +A callable 'initial' value is *not* used as a fallback if data is not provided. +In this example, we don't provide a value for 'username', and the form raises a +validation error rather than using the initial value for 'username'. +>>> p = UserRegistration({'password': 'secret'}, initial={'username': initial_django}) +>>> p.errors +{'username': [u'This field is required.']} +>>> p.is_valid() +False + +If a Form defines 'initial' *and* 'initial' is passed as a parameter to Form(), +then the latter will get precedence. +>>> class UserRegistration(Form): +... username = CharField(max_length=10, initial=initial_django) +... password = CharField(widget=PasswordInput) +>>> p = UserRegistration(auto_id=False) +>>> print p.as_ul() +<li>Username: <input type="text" name="username" value="django" maxlength="10" /></li> +<li>Password: <input type="password" name="password" /></li> +>>> p = UserRegistration(initial={'username': initial_stephane}, auto_id=False) +>>> print p.as_ul() +<li>Username: <input type="text" name="username" value="stephane" maxlength="10" /></li> +<li>Password: <input type="password" name="password" /></li> + # Help text ################################################################### You can specify descriptive text for a field by using the 'help_text' argument @@ -2869,7 +2955,7 @@ actual field name. {} >>> p.is_valid() True ->>> p.clean_data +>>> p.cleaned_data {'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)} Let's try submitting some bad data to make sure form.errors and field.errors @@ -2913,12 +2999,12 @@ of the same form. >>> p1 = Person(data, prefix='person1') >>> p1.is_valid() True ->>> p1.clean_data +>>> p1.cleaned_data {'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)} >>> p2 = Person(data, prefix='person2') >>> p2.is_valid() True ->>> p2.clean_data +>>> p2.cleaned_data {'first_name': u'Jim', 'last_name': u'Morrison', 'birthday': datetime.date(1943, 12, 8)} By default, forms append a hyphen between the prefix and the field name, but a @@ -2944,7 +3030,7 @@ self.prefix. >>> p = Person(data, prefix='foo') >>> p.is_valid() True ->>> p.clean_data +>>> p.cleaned_data {'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)} # Forms with NullBooleanFields ################################################ @@ -3006,16 +3092,16 @@ is different than its data. This is handled transparently, though. ... password1 = CharField(widget=PasswordInput) ... password2 = CharField(widget=PasswordInput) ... def clean(self): -... if self.clean_data.get('password1') and self.clean_data.get('password2') and self.clean_data['password1'] != self.clean_data['password2']: +... if self.cleaned_data.get('password1') and self.cleaned_data.get('password2') and self.cleaned_data['password1'] != self.cleaned_data['password2']: ... raise ValidationError(u'Please make sure your passwords match.') -... return self.clean_data +... return self.cleaned_data >>> def my_function(method, post_data): ... if method == 'POST': ... form = UserRegistration(post_data, auto_id=False) ... else: ... form = UserRegistration(auto_id=False) ... if form.is_valid(): -... return 'VALID: %r' % form.clean_data +... return 'VALID: %r' % form.cleaned_data ... t = Template('<form action="" method="post">\n<table>\n{{ form }}\n</table>\n<input type="submit" />\n</form>') ... return t.render(Context({'form': form})) @@ -3053,9 +3139,9 @@ VALID: {'username': u'adrian', 'password1': u'secret', 'password2': u'secret'} ... password1 = CharField(widget=PasswordInput) ... password2 = CharField(widget=PasswordInput) ... def clean(self): -... if self.clean_data.get('password1') and self.clean_data.get('password2') and self.clean_data['password1'] != self.clean_data['password2']: +... if self.cleaned_data.get('password1') and self.cleaned_data.get('password2') and self.cleaned_data['password1'] != self.cleaned_data['password2']: ... raise ValidationError(u'Please make sure your passwords match.') -... return self.clean_data +... return self.cleaned_data You have full flexibility in displaying form fields in a template. Just pass a Form instance to the template, and use "dot" access to refer to individual @@ -3321,7 +3407,7 @@ True </select> # MultiWidget and MultiValueField ############################################# -# MultiWidgets are widgets composed of other widgets. They are usually +# MultiWidgets are widgets composed of other widgets. They are usually # combined with MultiValueFields - a field that is composed of other fields. # MulitWidgets can themselved be composed of other MultiWidgets. # SplitDateTimeWidget is one example of a MultiWidget. @@ -3329,7 +3415,7 @@ True >>> class ComplexMultiWidget(MultiWidget): ... def __init__(self, attrs=None): ... widgets = ( -... TextInput(), +... TextInput(), ... SelectMultiple(choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), ... SplitDateTimeWidget(), ... ) @@ -3354,13 +3440,13 @@ True <input type="text" name="name_2_0" value="2007-04-25" /><input type="text" name="name_2_1" value="06:24:00" /> >>> class ComplexField(MultiValueField): -... def __init__(self, required=True, widget=None, label=None, initial=None): +... def __init__(self, required=True, widget=None, label=None, initial=None): ... fields = ( -... CharField(), +... CharField(), ... MultipleChoiceField(choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))), ... SplitDateTimeField() ... ) -... super(ComplexField, self).__init__(fields, required, widget, label, initial) +... super(ComplexField, self).__init__(fields, required, widget, label, initial) ... ... def compress(self, data_list): ... if data_list: @@ -3405,7 +3491,7 @@ ValidationError: [u'This field is required.'] </select> <input type="text" name="field1_2_0" value="2007-04-25" id="id_field1_2_0" /><input type="text" name="field1_2_1" value="06:24:00" id="id_field1_2_1" /></td></tr> ->>> f.clean_data +>>> f.cleaned_data {'field1': u'some text,JP,2007-04-25 06:24:00'} ################################# diff --git a/tests/regressiontests/serializers_regress/models.py b/tests/regressiontests/serializers_regress/models.py index c287b6e0d6..fea5c94cab 100644 --- a/tests/regressiontests/serializers_regress/models.py +++ b/tests/regressiontests/serializers_regress/models.py @@ -100,6 +100,12 @@ class Anchor(models.Model): something for other models to point at""" data = models.CharField(maxlength=30) + +class UniqueAnchor(models.Model): + """This is a model that can be used as + something for other models to point at""" + + data = models.CharField(unique=True, maxlength=30) class FKData(models.Model): data = models.ForeignKey(Anchor, null=True) @@ -116,6 +122,10 @@ class FKSelfData(models.Model): class M2MSelfData(models.Model): data = models.ManyToManyField('self', null=True, symmetrical=False) + +class FKDataToField(models.Model): + data = models.ForeignKey(UniqueAnchor, null=True, to_field='data') + # The following test classes are for validating the # deserialization of objects that use a user-defined # field as the primary key. diff --git a/tests/regressiontests/serializers_regress/tests.py b/tests/regressiontests/serializers_regress/tests.py index 97b3fbacbe..317739dac4 100644 --- a/tests/regressiontests/serializers_regress/tests.py +++ b/tests/regressiontests/serializers_regress/tests.py @@ -159,6 +159,7 @@ The end."""), (data_obj, 300, Anchor, "Anchor 1"), (data_obj, 301, Anchor, "Anchor 2"), + (data_obj, 302, UniqueAnchor, "UAnchor 1"), (fk_obj, 400, FKData, 300), # Post reference (fk_obj, 401, FKData, 500), # Pre reference @@ -184,8 +185,13 @@ The end."""), (m2m_obj, 445, M2MSelfData, []), (m2m_obj, 446, M2MSelfData, []), + (fk_obj, 450, FKDataToField, "UAnchor 1"), + (fk_obj, 451, FKDataToField, "UAnchor 2"), + (fk_obj, 452, FKDataToField, None), + (data_obj, 500, Anchor, "Anchor 3"), (data_obj, 501, Anchor, "Anchor 4"), + (data_obj, 502, UniqueAnchor, "UAnchor 2"), (pk_obj, 601, BooleanPKData, True), (pk_obj, 602, BooleanPKData, False), diff --git a/tests/regressiontests/test_client_regress/models.py b/tests/regressiontests/test_client_regress/models.py index c39fafe314..40d022a47a 100644 --- a/tests/regressiontests/test_client_regress/models.py +++ b/tests/regressiontests/test_client_regress/models.py @@ -60,7 +60,35 @@ class AssertTemplateUsedTests(TestCase): self.assertTemplateUsed(response, "Valid POST Template") except AssertionError, e: self.assertEquals(str(e), "Template 'Valid POST Template' was not one of the templates used to render the response. Templates used: ['form_view.html', 'base.html']") + +class AssertRedirectsTests(TestCase): + def test_redirect_page(self): + "An assertion is raised if the original page couldn't be retrieved as expected" + # This page will redirect with code 301, not 302 + response = self.client.get('/test_client/permanent_redirect_view/') + try: + self.assertRedirects(response, '/test_client/get_view/') + except AssertionError, e: + self.assertEquals(str(e), "Response didn't redirect as expected: Reponse code was 301 (expected 302)") + + def test_incorrect_target(self): + "An assertion is raised if the response redirects to another target" + response = self.client.get('/test_client/permanent_redirect_view/') + try: + # Should redirect to get_view + self.assertRedirects(response, '/test_client/some_view/') + except AssertionError, e: + self.assertEquals(str(e), "Response didn't redirect as expected: Reponse code was 301 (expected 302)") + def test_target_page(self): + "An assertion is raised if the reponse redirect target cannot be retrieved as expected" + response = self.client.get('/test_client/double_redirect_view/') + try: + # The redirect target responds with a 301 code, not 200 + self.assertRedirects(response, '/test_client/permanent_redirect_view/') + except AssertionError, e: + self.assertEquals(str(e), "Couldn't retrieve redirection page '/test_client/permanent_redirect_view/': response code was 301 (expected 200)") + class AssertFormErrorTests(TestCase): def test_unknown_form(self): "An assertion is raised if the form name is unknown" |
