Django ModelForm带有不在模型中的额外字段

72

我已经创建了一个ModelForm并添加了一些不在模型中的额外字段。当保存表单时,我使用这些字段进行一些计算。

这些额外的字段会出现在表单上,并且在上传表单时作为POST请求发送。问题是,当我验证表单时,它们未被添加到cleaned_data字典中。我该如何访问它们?


它们应该被添加到cleaned_data字典中。请发布您的表单代码,以便我们查看。 - gruszczy
如果你在回答中发布的代码是完全复制粘贴的,那么最后一行有缩进问题 - 它应该缩进一个级别。 - Daniel Roseman
我建议您不要将信息存储在表单字段中。更清洁的解决方案是:将其存储在会话中。 - sergzach
8个回答

52

编辑 2020(Django 2 或更高版本)

在 Django 2+ 中,您可以像这样添加额外的字段:

class ProfileForm(forms.ModelForm):
    
    extra_field = forms.ImageField()

    class Meta:
        model = User
        fields = ['username', 'country', 'website', 'biography']

翻译后的回答(Django 1)

可以通过添加额外字段来扩展Django ModelForm。假设您有一个自定义的用户模型和这个ModelForm

class ProfileForm(forms.ModelForm):

    class Meta:
        model = User
        fields = ['username', 'country', 'website', 'biography']

现在,想象一下你想要包含一个额外的字段(不在你的用户模型中,比如说一个图像头像)。通过以下方式扩展你的表单:

from django import forms

class AvatarProfileForm(ProfileForm):

    profile_avatar = forms.ImageField()

    class Meta(ProfileForm.Meta):
        fields = ProfileForm.Meta.fields + ('profile_avatar',)

最后(假设表单中有一个 ImageField ),在视图中实例化表单时,请记得包含 request.FILES

# (view.py)

def edit_profile(request):
    ...
    form = AvatarProfileForm(
        request.POST or None, 
        request.FILES or None, 
        instance=request.user
    )
    ...

希望这有所帮助。祝好运!
编辑:
在 AvatarProfileForm.Meta.fields 属性中,我遇到了“只能将元组(而不是‘列表’)连接到元组”的错误。将其更改为元组后,问题得到解决。

10
当我将my_new_field添加到fields列表中时,出现了django.core.exceptions.FieldError: Unknown field(s) (my_new_field) specified for MyModel错误。 Django 1.10版本。 - CpILL
3
为了让使用 Django 2、3 及以上版本的任何人都知道,这个技巧已不再需要。您只需像使用常规表单一样,在 ModelForm 中添加字段即可。 - lapin
在django 2.2.4中,我遇到了 TypeError: can only concatenate list (not "tuple") to list 错误。 你应该将第二个类表单中的 field 更改为列表格式。 - Ali Irani

17

我遇到了一个非常类似的问题,尽管看起来我已经做了所有必要的事情,但在 Django 启动时我仍然收到了这个错误:

django.core.exceptions.FieldError: Unknown field(s) (my_new_field) specified for MyModel

这是我的一个愚蠢错误,我不小心使用了 Widget 类来声明我的字段:

class MyForm(forms.ModelForm):
    my_new_field = forms.HiddenInput()

用一个字段(Field)类来代替:

class MyForm(forms.ModelForm):
    my_new_field = forms.CharField(widget=forms.HiddenInput())

这里不回答手头的问题(因为已经有很好的答案了),但可能会对其他人有所帮助。


2
这也是我的问题。我尝试使用 forms.CheckboxInput() 添加一个字段,但实际上需要使用 forms.BooleanField() - zoidberg
我试图添加“Textarea”,但犯了同样的错误! - Srinath Ganesh

8

首先在表单中添加字段

class CreateForm(forms.ModelForm):

extra_field     = forms.CharField(label = 'extra_field', required = False)

现在,在清理数据时,您需要从self.data中检索额外的字段,而不是self.cleaned_data。

正确的做法:

 self.data.get('extra_field')

错误:

 self.cleaned_data.get('extra_field')

3
如果你这样做,你的输入将不会被验证。 - lapin

5

首先,你不应该有artist_id和artist字段。它们是从模型构建的。如果你需要某个艺术家的名字,请添加artist_name字段,它是CharField

此外,在clean value中尝试从cleaned_data中检索某些数据可能不存在。你应该使用self.data中的值,其中包含直接来自POST的数据。


1
实际上,在单独的clean_X方法中使用cleaned_data是正确的:http://docs.djangoproject.com/en/dev/ref/forms/validation/#cleaning-a-specific-field-attribute - Anentropic

4

这个答案可能对原帖发布者来说已经太晚了,但我认为它可能会帮助其他人。我遇到了同样的问题,并注意到self.cleaned_data('artist_id')可以在clean()方法中访问,但不能在clean_artist()方法中访问。

当我在Meta的'fields'声明中添加了额外的字段时,它就起作用了。

    class Meta:
        model = Music
        fields=[..., 'artist_id']

您应该可以在clean_artist()函数中访问self.cleaned_data('artist_id')。


如果我在表单中添加了一个模型中不存在的额外字段,那么会出现“django.core.exceptions.FieldError: Unknown field(s) (xxx) specified for ModelName”的错误。 - Yang

3

好的,我已经解决了。似乎使用cleaned_data ['field_name']访问额外字段会引发KeyError,但是使用cleaned_data.get('field_name')可以工作。这很奇怪,因为模型的常规字段可以通过cleaned_data ['field_name']访问。

更新:不,它不起作用。使用get()不会引发KeyError,但它会将值设置为None,因为额外字段不在cleaned_data字典中。

这是代码。在模板中有一个自动完成,因此在表单中有一个“artist”字段呈现为CharField和一个隐藏的IntegerField,它将自动填充给定艺术家的ID。在clean_artist方法中,我想选择艺术家对象并将其存储在表单的艺术家字段中。

models.py

class Music(models.Model):
    artist = models.ForeignKey(Artist, related_name='music', blank=True, null=True)
    # other fields...

forms.py

class MusicForm(forms.ModelForm):
    artist_id = forms.IntegerField(label="", widget=forms.HiddenInput(), required=False)
    artist = forms.CharField(required=False)
    # other fields ...

    class Meta:
        model = Music

    def clean_artist(self):
        if self.cleaned_data.get('artist') == '':
            artist = None
        else:
            artist_id = self.cleaned_data.get('artist_id') # this returns always None because artist_id is not in cleaned_fields (this seemed to work with previous django versions but not with current SVN version)
            if artist_id != None:
                artist = Artist.objects.get(id=artist_id)
            else:
                artist = None

    return artist

1
这很奇怪,因为get与[]没有任何区别。请发布您的代码,这样更容易找到潜在的问题。 - gruszczy
4
返回艺术家需要额外的缩进。这不在 clean_artist 方法中。 - Brandon Henry
1
缩进修复了问题吗? - Anentropic

2
在Django 2中,您可以像普通表单一样添加字段。
class CreateCompanyForm(forms.ModelForm):

    password_confirmation = forms.CharField(
        label=translate('Password confirmation'),
        max_length=70,
        widget=forms.PasswordInput(),
        required=True,
    )
    company_name = forms.CharField(
        label="Nombre de la Compañía",
        max_length=90,
        widget=forms.TextInput(),
        required=True,
    )

    class Meta:
        model = AppUser
        fields = (
            "email",
            "first_name",
            "last_name",
            "password",
        )

0

我遇到了类似的问题,但在我的情况下,似乎额外的字段没有通过验证。这就是它未出现在表单的cleaned_data中的原因。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接