【问题标题】:Django modelformset creates new record instead of updating existing oneDjango modelformset 创建新记录而不是更新现有记录
【发布时间】:2012-07-25 20:25:27
【问题描述】:

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

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

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 处理新表单的方式有关。不知何故,它看起来总是试图为最后一个表单集创建一个新实例,无论它是否已经退出。

因此,如果我在添加最后一个表单集后不添加新表单集,模型表单集将尝试重新创建最后一个表单集(即使它是在上次提交时创建的)。

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

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

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

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

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

在我将此作为解决方案发布之前,我需要进行更多测试。

【问题讨论】:

  • 好的,我知道我的回答与您的问题无关。我会审核的。
  • 原理类似,需要更复杂的代码,因为我们处理的是modelformsets而不是forms。
  • 我已经改变了我的答案。请查看。

标签: django django-forms


【解决方案1】:

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

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

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()

您的代码中有一些附加功能。我展示了工作原理。了解解决方案就够了吗?

【讨论】:

  • 我不确定我是否理解您的回答。我不认为 method.name(不是 system.name)的唯一性是问题,它只是通过在 method 时产生 Method with this Name already exists 错误来使实际问题可见b> formset 已验证。真正的问题是,不知何故,我的代码每次都想创建一个新的 Method 实例,而不是更新现有的。
  • 感谢您提供新的解决方案。如果我理解正确,您将捕获 Method with this Name already exists 错误并提供解决方法。我可以将此作为最后的解决方案,但在我看来,代码存在更根本的问题,因为首先不应出现 Method with this Name already exists 错误。
  • @JohnPeters 我已将答案更改为解决方案,而无需检查“名称”键。现在够清楚了吗?
  • @JohnPeters 我认为这些限制与我们拥有 Django 模型和不知道最后一个的真实 SQL 数据库的情况有关。 Django 模型(没有初始化)不包含有关数据库当前状态的信息。每次我们想要更改行时都选择所有行是一项过于昂贵的操作
【解决方案2】:

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

【讨论】:

    猜你喜欢
    • 2023-01-12
    • 2021-03-30
    • 1970-01-01
    • 2011-12-10
    • 1970-01-01
    • 2020-07-08
    • 2020-06-20
    • 2010-11-23
    • 1970-01-01
    相关资源
    最近更新 更多