Django ModelForms中的额外字段:我能看到它们,但无法保存到模型中,为什么?

0 投票
2 回答
1457 浏览
提问于 2025-04-18 17:49

在我的Django应用中,我使用了一个ModelForm来让用户输入街道地址。我的目标是先清理这个地址,然后进行美国邮政服务(USPS)的验证,最后进行地理编码。如果这些步骤都成功了,我想把原始和清理后的地址都保存到我的Location模型中,模型的结构如下。

from localflavor.us.us_states import US_STATES
from django.contrib.gis.db import models

class Location(models.Model):
    # I want the user to enter these fields using a Model Form
    streetAddress = models.CharField(blank=False, null=False, max_length=300)
    city = models.CharField(null=False, blank=False,  max_length=50)
    state = USStateField(choices = US_STATES, null=False,)
    zip = models.CharField(null=False, max_length=10, )

    # I want to automatically derive these fields without any user intervention.
    # These fields are going to be calculated during the form validation process. 
    cleanedStreetAddress = models.CharField(blank=False, null=False, max_length=300)
    cleanedCity = models.CharField(null=True, blank=True,  max_length=50)
    cleanedState = USStateField(choices = US_STATES, null=False,)
    cleanedZip5 = models.CharField(null=False, max_length=5, )
    cleanedZip4 = models.CharField(null=False, max_length=4, )
    geoCoords = models.PointField(null=False, blank=False,)

在对应的ModelForm(叫做LocationForm)中,我设置了fields = ["streetAddress", "city", "state", "zip",]。其余的六个字段我通过手动添加到LocationForm.clean()方法返回的字典中来填充。到目前为止,一切都很好,没有问题。

在对应的基于类的视图中,我在form_valid()方法中处理这个表单,代码如下:

class InputLocationView(FormView):
    template_name = "helloWorld.html"
    form_class = LocationForm
    success_url = "/helloWorld"


    def form_valid(self, form):
        print "form.cleaned_data = %s" % form.cleaned_data
        ###
        # Output of the line above: 
        # form.cleaned_data = {'cleanedZip4': '0000', 'streetAddress': u'123 Main Street', 
        # 'zip': u'90210', 'cleanedStreetAddress': '123 MAIN ST', 'cleanedState': 'CO',
        # 'geoCoords': <Point object at 0x10a1b4e20>, 'state': u'CO', 'city': u'Anytown',
        # 'cleanedCity': 'ANYTOWN', 'cleanedZip5': '90210'}
        ###
        print "form.cleaned_data['geoCoords'] = (%s, %s)" % (form.cleaned_data['geoCoords'].x, form.cleaned_data['geoCoords'].y)
        # Output of the line above: form.cleaned_data['geoCoords'] = (-77.260768, 39.113707)

        newLocation = form.save(commit=False)
        newLocation.save()

从我上面的注释可以看到,我在LocationForm.clean()中添加的派生数据在这个基于类的视图中是存在的。这是个好消息。然而,当执行到newLocation.save()时,却出现了以下错误:

null value in column "geoCoords" violates not-null constraint
DETAIL:  Failing row contains (3, 123 Main Street, , Anytown, CO, 90210, , null,
 , , , null)

为什么newLocation.save()没有包含我所有的派生数据,而这些数据在form.cleaned_data字典中是明显存在的呢?我该如何确保所有来自form.cleaned_data的数据都能被包含在SQL插入语句中呢?

2 个回答

0

我记得没错的话,只有在 ModelForm.Meta.fields 列表中提到的字段,才会自动从 cleaned_data 中获取数据。与其把你自己计算出来的数据放进 cleaned_data,不如直接把它们设置到 self.instance 上,这样在你的 ModelForm 中会更简单。

1

ModelForm 这个工具大概只会填充你在 fields 中指定的那些字段。解决这个问题的方法是重写 ModelForm.save() 这个函数。可以这样做:

class LocationForm(ModelForm):
    ...
    def save(self, commit=True, **kwargs):
        # create the instance but don't commit it to the database
        location = super(LocationForm, self).save(commit=False, **kwargs)

        # now set your cleaned fields on the model instance
        location.cleanedStreetAddress = ...

        if commit: 
            location.save() 

        return location

想了解更多信息,可以查看 关于 save() 的文档

撰写回答