Commit b16dd1fe authored by Loic Bistuer's avatar Loic Bistuer
Browse files

Fixed #8620 -- Updated the Form metaclass to support excluding fields by shadowing them.

parent ac5ec7b8
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -90,6 +90,11 @@ class DeclarativeFieldsMetaclass(MediaDefiningClass):
            if hasattr(base, 'declared_fields'):
                declared_fields.update(base.declared_fields)

            # Field shadowing.
            for attr in base.__dict__.keys():
                if attr in declared_fields:
                    declared_fields.pop(attr)

        new_class.base_fields = declared_fields
        new_class.declared_fields = declared_fields

+7 −0
Original line number Diff line number Diff line
@@ -854,6 +854,13 @@ classes::
    <li>Instrument: <input type="text" name="instrument" /></li>
    <li>Haircut type: <input type="text" name="haircut_type" /></li>

.. versionadded:: 1.7

* It's possible to opt-out from a ``Field`` inherited from a parent class by
  shadowing it. While any non-``Field`` value works for this purpose, it's
  recommended to use ``None`` to make it explicit that a field is being
  nullified.

.. _form-prefix:

Prefixes for forms
+3 −0
Original line number Diff line number Diff line
@@ -293,6 +293,9 @@ Forms
  inheriting from both ``Form`` and ``ModelForm`` simultaneously have been
  removed as long as ``ModelForm`` appears first in the MRO.

* It's now possible to opt-out from a ``Form`` field declared in a parent class
  by shadowing it with a non-``Field`` value.

Internationalization
^^^^^^^^^^^^^^^^^^^^

+14 −0
Original line number Diff line number Diff line
@@ -651,6 +651,18 @@ There are a couple of things to note, however.
  because these classes rely on different metaclasses and a class can only have
  one metaclass.

.. versionadded:: 1.7

* It's possible to opt-out from a ``Field`` inherited from a parent class by
  shadowing it. While any non-``Field`` value works for this purpose, it's
  recommended to use ``None`` to make it explicit that a field is being
  nullified.

  You can only use this technique to opt out from a field defined declaratively
  by a parent class; it won't prevent the ``ModelForm`` metaclass from generating
  a default field. To opt-out from default fields, see
  :ref:`controlling-fields-with-fields-and-exclude`.

.. _modelforms-factory:

ModelForm factory function
@@ -749,6 +761,8 @@ instances of the model, you can specify an empty QuerySet::
   >>> AuthorFormSet(queryset=Author.objects.none())


.. _controlling-fields-with-fields-and-exclude:

Controlling which fields are used with ``fields`` and ``exclude``
-----------------------------------------------------------------

+23 −0
Original line number Diff line number Diff line
@@ -1815,3 +1815,26 @@ class ModelFormInheritanceTests(TestCase):
                fields = '__all__'

        self.assertEqual(list(ModelForm().fields.keys()), ['name', 'age'])

    def test_field_shadowing(self):
        class ModelForm(forms.ModelForm):
            class Meta:
                model = Writer
                fields = '__all__'

        class Mixin(object):
            age = None

        class Form(forms.Form):
            age = forms.IntegerField()

        class Form2(forms.Form):
            foo = forms.IntegerField()

        self.assertEqual(list(ModelForm().fields.keys()), ['name'])
        self.assertEqual(list(type(str('NewForm'), (Mixin, Form), {})().fields.keys()), [])
        self.assertEqual(list(type(str('NewForm'), (Form2, Mixin, Form), {})().fields.keys()), ['foo'])
        self.assertEqual(list(type(str('NewForm'), (Mixin, ModelForm, Form), {})().fields.keys()), ['name'])
        self.assertEqual(list(type(str('NewForm'), (ModelForm, Mixin, Form), {})().fields.keys()), ['name'])
        self.assertEqual(list(type(str('NewForm'), (ModelForm, Form, Mixin), {})().fields.keys()), ['name', 'age'])
        self.assertEqual(list(type(str('NewForm'), (ModelForm, Form), {'age': None})().fields.keys()), ['name'])