Django: Remove a field from a Form subclass

2019-01-23 23:26发布

问题:

class LoginForm(forms.Form):
    nickname = forms.CharField(max_length=100)
    username = forms.CharField(max_length=100)
    password = forms.CharField(widget=forms.PasswordInput)


class LoginFormWithoutNickname(LoginForm):
    # i don't want the field nickname here
    nickname = None #??

Is there a way to achieve this?

Note: i don't have a ModelForm, so the Meta class with exclude doesn't work.

回答1:

You can alter the fields in a subclass by overriding the init method:

class LoginFormWithoutNickname(LoginForm):
    def __init__(self, *args, **kwargs):
        super(LoginFormWithoutNickname, self).__init__(*args, **kwargs)
        self.fields.pop('nickname')


回答2:

Django 1.7 addressed this in commit b16dd1fe019 for ticket #8620. In Django 1.7, it becomes possible to do nickname = None in the subclass as the OP suggests. From the documentation changes in the commit:

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.



回答3:

I found that, please comment if interested.

(in Django 1.7.4) extending forms, with following code:

class MyForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        super(MyForm, self).__init__(*args, **kwargs)

        for key, field in self.fields.iteritems():
            self.fields[key].required = False

    class Meta:
        model = MyModel
        exclude = []

    field_1 = forms.CharField(label="field_1_label")
    field_2 = forms.CharField(label="field_2_label", widget=forms.Textarea(attrs={'class': 'width100 h4em'}),)
    field_3 = forms.CharField(label="field_3_label", widget=forms.TextInput(attrs={'class': 'width100'}),)
    field_4 = forms.ModelChoiceField(label='field_4_label', queryset=AnotherModel.objects.all().order_by("order") )

class MyForm_Extended_1(MyForm):
    field_1 = None


class MyForm_Extended_2(MyForm):
    class Meta:
        model = MyModel
        exclude =[
                    'field_1',
                ]

MyForm_Extended_1 set field_1 as None, (the column in db is updated as Null)

MyForm_Extended_2 ignore the field (ignore the column in db during the save)

So, for my purpose, I use the second method.



回答4:

I didn't like the fact (or so I understood) that the exclusion of a field in the second class using "class Meta:" still results in the unused field being in the db.

Perhaps the simplest way is to define an abstract class that has the fields shared by both classes. Then the two original classes above become subclasses of this new class. So the example given at the start of this thread might look the following. It's a bit more code, but this way you extend a subclass rather than do an (incomplete) exclusion from a super class.

class LoginForm_Common(forms.Form):
    username = forms.CharField(max_length=100)
    password = forms.CharField(widget=forms.PasswordInput)
    class Meta:
        abstract = True

class LoginForm(LoginForm_Common):
    nickname = forms.CharField(max_length=100)

class LoginFormWithoutNickname(LoginForm_Common):
    pass