Loading django/forms/forms.py +5 −0 Original line number Diff line number Diff line Loading @@ -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 Loading docs/ref/forms/api.txt +7 −0 Original line number Diff line number Diff line Loading @@ -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 Loading docs/releases/1.7.txt +3 −0 Original line number Diff line number Diff line Loading @@ -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 ^^^^^^^^^^^^^^^^^^^^ Loading docs/topics/forms/modelforms.txt +14 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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`` ----------------------------------------------------------------- Loading tests/model_forms/tests.py +23 −0 Original line number Diff line number Diff line Loading @@ -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']) Loading
django/forms/forms.py +5 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
docs/ref/forms/api.txt +7 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
docs/releases/1.7.txt +3 −0 Original line number Diff line number Diff line Loading @@ -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 ^^^^^^^^^^^^^^^^^^^^ Loading
docs/topics/forms/modelforms.txt +14 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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`` ----------------------------------------------------------------- Loading
tests/model_forms/tests.py +23 −0 Original line number Diff line number Diff line Loading @@ -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'])