Ticket #5524: 5524-new-4.patch

File 5524-new-4.patch, 13.7 KB (added by Simon Charette, 12 years ago)

Doc update and unicode_literals related changes

  • django/contrib/formtools/tests/__init__.py

    From bc5e4754ebf7416c53459a051511b038998d7c01 Mon Sep 17 00:00:00 2001
    From: Simon Charette <charette.s@gmail.com>
    Date: Sun, 22 Jul 2012 00:36:32 -0400
    Subject: [PATCH] Fixed #5524 -- Invalid forms should keep their valid
     `cleaned_data`.
    
    Thanks to @aaugustin for the patch.
    ---
     django/contrib/formtools/tests/__init__.py |    2 +-
     django/forms/forms.py                      |    2 --
     django/forms/models.py                     |   25 ++++++++++++-------------
     docs/ref/forms/api.txt                     |   21 ++++++++++++---------
     docs/ref/forms/validation.txt              |   10 ++++++----
     docs/releases/1.5.txt                      |   10 ++++++++++
     tests/modeltests/model_forms/tests.py      |    3 +--
     tests/regressiontests/forms/tests/forms.py |   24 ++++--------------------
     8 files changed, 46 insertions(+), 51 deletions(-)
    
    diff --git a/django/contrib/formtools/tests/__init__.py b/django/contrib/formtools/tests/__init__.py
    index 3bccb55..ee93479 100644
    a b class WizardTests(TestCase):  
    317317
    318318        class WizardWithProcessStep(TestWizardClass):
    319319            def process_step(self, request, form, step):
    320                 that.assertTrue(hasattr(form, 'cleaned_data'))
     320                that.assertTrue(form.is_valid())
    321321                reached[0] = True
    322322
    323323        wizard = WizardWithProcessStep([WizardPageOneForm,
  • django/forms/forms.py

    diff --git a/django/forms/forms.py b/django/forms/forms.py
    index 7942275..9288297 100644
    a b class BaseForm(StrAndUnicode):  
    270270        self._clean_fields()
    271271        self._clean_form()
    272272        self._post_clean()
    273         if self._errors:
    274             del self.cleaned_data
    275273
    276274    def _clean_fields(self):
    277275        for name, field in self.fields.items():
  • django/forms/models.py

    diff --git a/django/forms/models.py b/django/forms/models.py
    index 26f8691..7d1809d 100644
    a b class BaseModelFormSet(BaseFormSet):  
    504504        all_unique_checks = set()
    505505        all_date_checks = set()
    506506        for form in self.forms:
    507             if not hasattr(form, 'cleaned_data'):
     507            if not form.is_valid():
    508508                continue
    509509            exclude = form._get_validation_exclusions()
    510510            unique_checks, date_checks = form.instance._get_unique_checks(exclude=exclude)
    class BaseModelFormSet(BaseFormSet):  
    516516        for uclass, unique_check in all_unique_checks:
    517517            seen_data = set()
    518518            for form in self.forms:
    519                 # if the form doesn't have cleaned_data then we ignore it,
    520                 # it's already invalid
    521                 if not hasattr(form, "cleaned_data"):
     519                if not form.is_valid():
    522520                    continue
    523521                # get data for each field of each of unique_check
    524522                row_data = tuple([form.cleaned_data[field] for field in unique_check if field in form.cleaned_data])
    525523                if row_data and not None in row_data:
    526                     # if we've aready seen it then we have a uniqueness failure
     524                    # if we've already seen it then we have a uniqueness failure
    527525                    if row_data in seen_data:
    528526                        # poke error messages into the right places and mark
    529527                        # the form as invalid
    530528                        errors.append(self.get_unique_error_message(unique_check))
    531529                        form._errors[NON_FIELD_ERRORS] = self.error_class([self.get_form_error()])
    532                         del form.cleaned_data
    533                         break
     530                        # remove the data from the cleaned_data dict since it was invalid
     531                        for field in unique_check:
     532                            if field in form.cleaned_data:
     533                                del form.cleaned_data[field]
    534534                    # mark the data as seen
    535535                    seen_data.add(row_data)
    536536        # iterate over each of the date checks now
    class BaseModelFormSet(BaseFormSet):  
    538538            seen_data = set()
    539539            uclass, lookup, field, unique_for = date_check
    540540            for form in self.forms:
    541                 # if the form doesn't have cleaned_data then we ignore it,
    542                 # it's already invalid
    543                 if not hasattr(self, 'cleaned_data'):
     541                if not form.is_valid():
    544542                    continue
    545543                # see if we have data for both fields
    546544                if (form.cleaned_data and form.cleaned_data[field] is not None
    class BaseModelFormSet(BaseFormSet):  
    554552                    else:
    555553                        date_data = (getattr(form.cleaned_data[unique_for], lookup),)
    556554                    data = (form.cleaned_data[field],) + date_data
    557                     # if we've aready seen it then we have a uniqueness failure
     555                    # if we've already seen it then we have a uniqueness failure
    558556                    if data in seen_data:
    559557                        # poke error messages into the right places and mark
    560558                        # the form as invalid
    561559                        errors.append(self.get_date_error_message(date_check))
    562560                        form._errors[NON_FIELD_ERRORS] = self.error_class([self.get_form_error()])
    563                         del form.cleaned_data
    564                         break
     561                        # remove the data from the cleaned_data dict since it was invalid
     562                        del form.cleaned_data[field]
     563                    # mark the data as seen
    565564                    seen_data.add(data)
    566565        if errors:
    567566            raise ValidationError(errors)
  • docs/ref/forms/api.txt

    diff --git a/docs/ref/forms/api.txt b/docs/ref/forms/api.txt
    index 50488b0..777d73e 100644
    a b Note that any text-based field -- such as ``CharField`` or ``EmailField`` --  
    199199always cleans the input into a Unicode string. We'll cover the encoding
    200200implications later in this document.
    201201
    202 If your data does *not* validate, your ``Form`` instance will not have a
    203 ``cleaned_data`` attribute::
     202If your data does *not* validate, the ``cleaned_data`` dictionary contains
     203only the valid fields::
    204204
    205205    >>> data = {'subject': '',
    206206    ...         'message': 'Hi there',
    If your data does *not* validate, your ``Form`` instance will not have a  
    210210    >>> f.is_valid()
    211211    False
    212212    >>> f.cleaned_data
    213     Traceback (most recent call last):
    214     ...
    215     AttributeError: 'ContactForm' object has no attribute 'cleaned_data'
     213    {'cc_myself': True, 'message': u'Hi there'}
     214
     215.. versionchanged:: 1.5
     216
     217Until Django 1.5, the ``cleaned_data`` attribute wasn't defined at all when
     218the ``Form`` didn't validate.
    216219
    217220``cleaned_data`` will always *only* contain a key for fields defined in the
    218221``Form``, even if you pass extra data when you define the ``Form``. In this
    but ``cleaned_data`` contains only the form's fields::  
    232235    >>> f.cleaned_data # Doesn't contain extra_field_1, etc.
    233236    {'cc_myself': True, 'message': u'Hi there', 'sender': u'foo@example.com', 'subject': u'hello'}
    234237
    235 ``cleaned_data`` will include a key and value for *all* fields defined in the
    236 ``Form``, even if the data didn't include a value for fields that are not
    237 required. In this example, the data dictionary doesn't include a value for the
     238When the ``Form`` is valid, ``cleaned_data`` will include a key and value for
     239*all* its fields, even if the data didn't include a value for some optional
     240fields. In this example, the data dictionary doesn't include a value for the
    238241``nick_name`` field, but ``cleaned_data`` includes it, with an empty value::
    239242
    240243    >>> class OptionalPersonForm(Form):
    lazy developers -- they're not the only way a form object can be displayed.  
    583586
    584587   Used to display HTML or access attributes for a single field of a
    585588   :class:`Form` instance.
    586    
     589
    587590   The :meth:`__unicode__` and :meth:`__str__` methods of this object displays
    588591   the HTML for this field.
    589592
  • docs/ref/forms/validation.txt

    diff --git a/docs/ref/forms/validation.txt b/docs/ref/forms/validation.txt
    index 97883d7..95424d0 100644
    a b Secondly, once we have decided that the combined data in the two fields we are  
    362362considering aren't valid, we must remember to remove them from the
    363363``cleaned_data``.
    364364
    365 In fact, Django will currently completely wipe out the ``cleaned_data``
    366 dictionary if there are any errors in the form. However, this behavior may
    367 change in the future, so it's not a bad idea to clean up after yourself in the
    368 first place.
     365.. versionchanged:: 1.5
     366
     367Django used to remove the ``cleaned_data`` attribute entirely if there were
     368any errors in the form. Since version 1.5, ``cleaned_data`` is present even if
     369the form doesn't validate, but it contains only field values that did
     370validate.
  • docs/releases/1.5.txt

    diff --git a/docs/releases/1.5.txt b/docs/releases/1.5.txt
    index fd9ae4f..924d3ba 100644
    a b Session not saved on 500 responses  
    188188Django's session middleware will skip saving the session data if the
    189189response's status code is 500.
    190190
     191`cleaned_data` dictionary kept for invalid forms
     192~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     193
     194The :attr:`~django.forms.Form.cleaned_data` dictionary is now always present
     195after form validation. When the form doesn't validate, it contains only the
     196fields that passed validation. You should test the success of the validation
     197with the :meth:`~django.forms.Form.is_valid()` method and not with the
     198presence or absence of the :attr:`~django.forms.Form.cleaned_data` attribute
     199on the form.
     200
    191201Miscellaneous
    192202~~~~~~~~~~~~~
    193203
  • tests/modeltests/model_forms/tests.py

    diff --git a/tests/modeltests/model_forms/tests.py b/tests/modeltests/model_forms/tests.py
    index 281316a..6e88ff1 100644
    a b class OldFormForXTests(TestCase):  
    637637        f = BaseCategoryForm({'name': '', 'slug': 'not a slug!', 'url': 'foo'})
    638638        self.assertEqual(f.errors['name'], ['This field is required.'])
    639639        self.assertEqual(f.errors['slug'], ["Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens."])
    640         with self.assertRaises(AttributeError):
    641             f.cleaned_data
     640        self.assertEqual(f.cleaned_data, {'url': 'foo'})
    642641        with self.assertRaises(ValueError):
    643642            f.save()
    644643        f = BaseCategoryForm({'name': '', 'slug': '', 'url': 'foo'})
  • tests/regressiontests/forms/tests/forms.py

    diff --git a/tests/regressiontests/forms/tests/forms.py b/tests/regressiontests/forms/tests/forms.py
    index 7e1c838..a8a28ba 100644
    a b class FormsTestCase(TestCase):  
    8282        self.assertEqual(p.errors['last_name'], ['This field is required.'])
    8383        self.assertEqual(p.errors['birthday'], ['This field is required.'])
    8484        self.assertFalse(p.is_valid())
    85         try:
    86             p.cleaned_data
    87             self.fail('Attempts to access cleaned_data when validation fails should fail.')
    88         except AttributeError:
    89             pass
     85        self.assertEqual(p.cleaned_data, {})
    9086        self.assertHTMLEqual(str(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>
    9187<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>
    9288<tr><th><label for="id_birthday">Birthday:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="birthday" id="id_birthday" /></td></tr>""")
    class FormsTestCase(TestCase):  
    145141  * This field is required.
    146142* birthday
    147143  * This field is required.""")
    148         try:
    149             p.cleaned_data
    150             self.fail('Attempts to access cleaned_data when validation fails should fail.')
    151         except AttributeError:
    152             pass
     144        self.assertEqual(p.cleaned_data, {'last_name': 'Lennon'})
    153145        self.assertEqual(p['first_name'].errors, ['This field is required.'])
    154146        self.assertHTMLEqual(p['first_name'].errors.as_ul(), '<ul class="errorlist"><li>This field is required.</li></ul>')
    155147        self.assertEqual(p['first_name'].errors.as_text(), '* This field is required.')
    class FormsTestCase(TestCase):  
    16781670        form = SongForm(data, empty_permitted=False)
    16791671        self.assertFalse(form.is_valid())
    16801672        self.assertEqual(form.errors, {'name': ['This field is required.'], 'artist': ['This field is required.']})
    1681         try:
    1682             form.cleaned_data
    1683             self.fail('Attempts to access cleaned_data when validation fails should fail.')
    1684         except AttributeError:
    1685             pass
     1673        self.assertEqual(form.cleaned_data, {})
    16861674
    16871675        # Now let's show what happens when empty_permitted=True and the form is empty.
    16881676        form = SongForm(data, empty_permitted=True)
    class FormsTestCase(TestCase):  
    16961684        form = SongForm(data, empty_permitted=False)
    16971685        self.assertFalse(form.is_valid())
    16981686        self.assertEqual(form.errors, {'name': ['This field is required.']})
    1699         try:
    1700             form.cleaned_data
    1701             self.fail('Attempts to access cleaned_data when validation fails should fail.')
    1702         except AttributeError:
    1703             pass
     1687        self.assertEqual(form.cleaned_data, {'artist': 'The Doors'})
    17041688
    17051689        # If a field is not given in the data then None is returned for its data. Lets
    17061690        # make sure that when checking for empty_permitted that None is treated
Back to Top