#8620 closed New feature (fixed)
ModelForm.Meta.exclude only excludes model fields, not form fields
Reported by: | levity | Owned by: | loic84 |
---|---|---|---|
Component: | Forms | Version: | dev |
Severity: | Normal | Keywords: | |
Cc: | tom@…, tom@…, cody.somerville@…, mmitar@…, loic@… | Triage Stage: | Accepted |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
If I have a ModelForm that inherits from another form, and I define exclude and/or fields, these only affect fields on the model. I cannot use them to exclude any non-model fields that I may have added to the ancestor form. E.g.:
class AForm(ModelForm): extra_non_model_field = forms.CharField() class BForm(AForm): class Meta: model = Foo exclude = ('extra_non_model_field',) # this doesn't work, due to the way ModelFormMetaclass.__new__ is written
Sure that's a pathological example, but there are more sensible situations in which this is an issue. Seems to me it's more intuitive to expect the exclude/fields options to affect all fields, not just model fields.
A fix would be to add this at django/forms/models.py line 180:
[declared_fields.pop(f) for f in declared_fields.keys() if f not in opts.fields or f in opts.exclude]
Attachments (3)
Change History (28)
comment:1 by , 16 years ago
comment:2 by , 16 years ago
Component: | Uncategorized → Forms |
---|
comment:3 by , 16 years ago
Triage Stage: | Unreviewed → Accepted |
---|
comment:4 by , 16 years ago
- patch should be changed a bit to avoid "NoneType is not iterable" error.
[declared_fields.pop(f) for f in declared_fields.keys() if f not in (opts.fields or []) or f in (opts.exclude or [])]
- this patch needs improvement - if fields aren't set in Meta class, overriding the default field types does not work.
comment:6 by , 15 years ago
Has patch: | set |
---|---|
Needs tests: | set |
comment:7 by , 14 years ago
Cc: | added |
---|
comment:8 by , 14 years ago
Owner: | changed from | to
---|
by , 14 years ago
Attachment: | django_model_forms_excluding_fields_fix_2.diff added |
---|
updated patch with tests based on r15800
comment:9 by , 14 years ago
Needs documentation: | set |
---|---|
Patch needs improvement: | set |
I've attached an updated patch that includes tests for the new behavior. However, this change in behavior breaks several other tests. One newly-failing test is also in the modelform tests (tests/regressiontests/forms/tests/models.py), but several are elsewhere in the test suite.
That suggests to me that there may be lots of code built on the assumption that the Meta.exclude attribute doesn't affect fields explicitly declared in a superclass. While changing this behavior seems useful to me, it probably merits more consideration, to either OK the change and accept the consequences for users, or to provide an alternate way to achieve exclusion of fields inherited from superclasses (which may arguably be useful outside of ModelForms).
The patch still needs work (changes to several other tests that fail as a result of the change).
comment:10 by , 14 years ago
Severity: | → Normal |
---|---|
Type: | → New feature |
comment:11 by , 14 years ago
Cc: | added |
---|---|
Easy pickings: | unset |
comment:12 by , 13 years ago
UI/UX: | unset |
---|
The problem still occurs in Django 1.3
Also, if you set form fields to Required=False, they are still required in form validation.
Here is an example :
class RegistrationForm(forms.ModelForm): #---------------------------------------------------- Extra form fields email_confirm = forms.EmailField(widget = forms.TextInput(attrs = {'size':20})) password_confirm = forms.CharField(required=False, widget = forms.PasswordInput(attrs = {'size':20})) eula = forms.BooleanField(required=False, label=u"CGU") #=========================================================================== # Metadata #=========================================================================== class Meta: model = User fields = ('username', 'email', 'email_confirm', 'password', 'first_name', 'last_name')
comment:13 by , 13 years ago
Needs documentation: | unset |
---|---|
Needs tests: | unset |
Patch needs improvement: | unset |
Added an updated patch, that only implements leaving out an explicitly declared field with Meta.exclude, and does not require adding explicitly declared fields into the Meta.fields list like the previous patch did (which was backwards incompatible).
Added a note to the documentation also.
comment:14 by , 13 years ago
See duplicate ticket:13971 for additional discussion/patches on this issue.
comment:15 by , 13 years ago
Cc: | added |
---|
comment:16 by , 13 years ago
Cc: | added |
---|
comment:17 by , 11 years ago
I think this should be fine backwards compatibility-wise, but a posted a PR just to give greater visibility to the patch before I commit it.
comment:18 by , 11 years ago
At the moment, I'm definitely -0 on this change in functionality. I believe that fields
and exclude
are shortcuts and are secondary to actually defining the fields - i.e. the field definitions on the form should *always* take precedence. I believe that the subclassing issue can be solved by setting username = None
on the subclass.
In particular this introduces a disparity between Form
and ModelForm
- I don't want to recommend this as a subclassing route if it doesn't actually work in the general case.
comment:19 by , 11 years ago
This may or may not be the right solution to this issue, but this PR allows shadowing a declarative field and would enable the username = None
example from the previous comment: https://github.com/django/django/pull/1382.
comment:20 by , 11 years ago
Cc: | added |
---|
comment:21 by , 11 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
comment:24 by , 11 years ago
Resolution: | → fixed |
---|---|
Status: | assigned → closed |
Tthis also fails to work for model fields that are modified in the ancestor form, e.g.: