使用Django formset保存具有唯一属性的多个对象

时间:2017-09-29 08:14:40

标签: django django-forms unique-constraint

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

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

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

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

  • 设置1:machine = 1;顺序= 1
  • 设置2:machine = 1;顺序= 2

如果我要切换两个Setting的订单:

  • 设置1:machine = 1;顺序= 2
  • 设置2:machine = 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在前端解决这个问题,但我希望检查我的模型/表单。循环使用formset行似乎是可能的,但很难看?

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

2 个答案:

答案 0 :(得分:1)

似乎无法限制数据库级别的顺序。我不是说它不可能,但我不知道如何。

现在,我在我的formset中添加了一张支票。如果有人遇到类似的问题,我会把它留在这里。

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)

答案 1 :(得分:1)

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

请查看有关如何设置此答案的答案:https://stackoverflow.com/a/56644496

然后您要:

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