【问题标题】:Save multiple objects with a unique attribute using Django formset使用 Django formset 保存具有唯一属性的多个对象
【发布时间】:2017-09-29 08:14:40
【问题描述】:

TL;DR:如何使用表单集保存/验证具有唯一属性的多个对象?

假设我有一个Machine,它有多个Settings(参见下面的模型)。这些设置在机器内应该有一个唯一的顺序。这可以通过在Setting 模型的Meta 类中定义unique_together 属性来实现:

unique_together = (('order', 'machine'), )

但是,我使用内联表单集来更新 Machine 的所有 Settings。假设我的表单集中有以下信息:

  • 设置 1:机器=1;订单=1
  • 设置 2:机器=1;订单=2

如果我要在两个Settings 上切换顺序:

  • 设置 1:机器=1;订单=2
  • 设置 2:机器=1;订单=1

并使用以下代码保存表单:

self.object = machine_form.save()
settings = setting_formset.save(commit=False)
for setting in settings:
    setting.machine = self.object
    setting.save()

然后我得到一个唯一约束错误,因为我尝试使用 order=2 保存设置 1,但设置 2 仍然具有该顺序。我似乎无法为这个问题找到一个干净的解决方案。我想保留unique_together 约束以确保我的数据的正确性。我知道如何在前端使用 Javascript 解决这个问题,但我想检查我的模型/表单。循环遍历表单集行似乎是可能的,但很难看?

class Machine(models.Model):
    name = models.CharField(max_length=255)

class Setting(models.Model):
    name = models.CharField(max_length=255)
    machine = models.ForeignKey(Machine, on_delete=models.CASCADE)
    order = models.PositiveSmallIntegerField()

【问题讨论】:

    标签: django django-forms unique-constraint


    【解决方案1】:

    似乎无法在数据库级别限制顺序。我不是说不可能,但我不知道怎么做。

    现在,我在表单集中添加了一个检查。我会把它留在这里,以防有人遇到类似问题。

    class BaseSettingFormSet(BaseInlineFormSet):
        '''FormSet to update the order of Settings in a Machine'''
    
        def clean(self):
            '''Checks if there are no two Settings with the same order.'''
            if any(self.errors):
                return
            orders = []
            for form in self.forms:
                if form.cleaned_data:
                    order = form.cleaned_data['order']
                    if order in orders:
                        form.add_error('order', _('The order of each setting must be unique'))
                    else:
                        orders.append(order)
    

    【讨论】:

      【解决方案2】:

      我能够使用延迟约束来实现它(仅适用于 Postgres DB)

      查看此答案以了解如何设置:https://stackoverflow.com/a/56644496

      然后你想:

      1. 在 ModelForm 中设置 self._validate_unique = False(在 clean() 方法中设置为 True
      2. 将表单集保存在transaction.atomic() 块中(这将触发延迟约束)

      【讨论】:

        猜你喜欢
        • 2019-07-21
        • 1970-01-01
        • 2023-04-02
        • 2019-04-15
        • 2013-06-25
        • 1970-01-01
        • 2017-02-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多