Django modelformset创建新记录而不是更新现有记录

时间:2012-07-27 07:11:47

标签: django django-forms

我有一个可以有一个或多个模型的系统。我已经在数据库中使用manytomany字段建模了这种关系。下面的代码用于以单一形式编辑系统及其相关方法。

添加方法,方法是填写表单并按提交仅<第一次 。如果我做了一个小改动并再次提交,我会收到以下消息(由下面的代码生成):

METHODFORMSET.ERRORS: [{}, {'name': [u'Method with this Name already exists.']}]

这是因为名称字段是唯一的,但它应该更新,而不是创建新记录,即使我使用的是POST数据生成methodformset实例...

请注意,此行为仅适用于最后添加的方法实例,而不适用于表中已存在的实例。

以下是相关代码,有谁能让我知道我做错了什么?

def sysedit(request, sys_id):

    system = System.objects.get(id=sys_id)
    MethodFormSet = modelformset_factory(Method, form=MethodForm)

    post = None
    if request.POST:
        post = request.POST.copy()
        if 'add_method' in request.POST:
            post['method-TOTAL_FORMS'] = repr(int(
                                                post['method-TOTAL_FORMS'])+ 1)

    systemform = SystemForm(data=post, instance=system)

    methodformset = MethodFormSet(data=post, prefix='method',
            queryset=Method.objects.filter(id__in=system.method.all()))

    if methodformset.is_valid():
        mfs = methodformset.save()
        print 'SAVED-method', mfs
        for mf in mfs:
            if systemform.is_valid():
                sp = systemform.save(mf)
                print 'SYSTEM', sp
            else:
                print 'SYSFORMSET.ERRORS:', systemform.errors
    else:
        print 'METHODFORMSET.ERRORS:', methodformset.errors

    return render_to_response('sysedit.html', 
            {'systemform': systemform, 
            'methodformset': methodformset, 
            'system': system},
            context_instance=RequestContext(request))


class System(models.Model):
    method = models.ManyToManyField(Method)
    ...

class Method(models.Model):
    name = models.CharField(unique=True)
    ...

class MethodForm(ModelForm):
    class Meta:
        model = Method

class SystemForm(ModelForm):
    def save(self, new_method=None, commit=True, *args, **kwargs):
        m = super(SystemForm, self).save(commit=False, *args, **kwargs)
        if new_method:
            m.method.add(new_method)
        if commit:
            m.save()
        return m

    class Meta:
        model = System
        exclude = ('method')

[在Sergzach回答之后编辑]:

问题不在于如何处理Method with this name already exists错误,而是为了防止首先发生这种错误。我认为实际问题可能与modelformsets处理新表单的方式有关。不知何故,它看起来总是试图为最后一个formset创建一个新实例,无论它是否已经退出。

因此,如果我在添加最后一个之后没有添加新的formset,那么modelformset将尝试重新创建最后一个(即使它刚刚在之前的提交中创建)。

最初的情况是我在methodformset中有1个有效的Method实例和1个新的未绑定实例。然后我填写表单并点击保存,它会验证两个方法并绑定第二个,然后保存到表中。 到目前为止一切都很顺利,但如果我然后第二次点击保存错误发生。也许这与method-TOTAL_FORMS = 2和method-INITIAL_FORMS = 1的事实有关。难道这会导致modelformset在第二个方法上强制创建吗?

任何人都可以确认/否认这个吗?

[在不看代码的周末后编辑]:

问题是由于我在视图中保存表单并保存后,我将原始的methodformset实例(从之前保存)发送到模板。通过使用queryset而不是POST数据在保存之后重新实例化,可以解决该问题。

因此,防止这样的错误的一般规则是要么在保存后转到不同的页面(完全避免它),要么使用上述解决方案。

在我发布此解决方案之前,我需要做更多测试。

2 个答案:

答案 0 :(得分:0)

您可以在保存表单集时验证每个表单。我创建了一个简单的例子(类似于你的代码),它对我很有用。如果没有具有此类名称的对象,则会创建新对象,否则会编辑现有对象。

您需要一个表单来编辑模型对象:

class EditMethodForm( forms.ModelForm ):
   class Meta:
      model = Method
      exclude = ( 'name', )

然后代替methodformset.is_valid()执行下一步:

for methodform in methodformset:
    try:
        instance = Method.objects.get( name = request.POST[ 'name' ] )
    except Method.DoesNotExist:
        methodform.save()
    else:
        editmethodform = EditMethodForm( request.POST, instance = instance )
        if editmethodform.is_valid():
            editmethodform.save()

您的代码中还有一些其他功能。我展示了工作原理。是否足以理解解决方案?

答案 1 :(得分:0)

我通过在保存后重新实例化modelformset解决了这个问题(请参阅问题底部的编辑)