Django 管理员内联唯一约束在编辑时违反



我有一个内联的表单。相关模型具有唯一的组合约束(parent_id,数字)。我有一个有两个孩子的实体

parent_id | number |
1 |      1 |
1 |      2 |

我正在尝试在一次操作中将这些孩子编辑到状态

parent_id | number |
1 |      2 |
1 |      3 |

当我保存时,我在第一个条目上出现错误:Child with this parent_id and number already exists.

但是,如果我首先编辑第二个条目:

parent_id | number |
1 |      1 |
1 |      3 |

然后是第一个

parent_id | number |
1 |      2 |
1 |      3 |

在两个单独的操作中,它工作正常。

家长管理员定义

class ParentAdmin(admin.ModelAdmin):
form = BaseForm
inlines = [LevelExerciseInline]

子级内联定义

class ChildInline(admin.StackedInline):
form = BaseForm
model = Child
extra = 3

当你使用 Django Admin inline 时,情况不会如下:

我正在尝试在一次操作中将这些孩子编辑到状态

内联是完整表单的一部分,它们将按照它们在表单中出现的顺序进行处理。因此,即使你"在一个动作中"更改它们,这意味着你在一个表单 POST 中更改它们,Django 仍然会一个接一个地保存这些相关对象:

将保存列表中的第一个内联,并尝试保存parent_id

数字为 2 的 1,而数据库中仍有数字为 2 的 parent_id 2。具体而言,使用以下代码调用formset.save()(https://github.com/django/django/blob/master/django/forms/models.py#L655)。

注意:在这里,"form"是实际HTML表单的一部分(它只是变量的名称,不要混淆它)。

def save(self, commit=True):
"""Saves model instances for every form, adding and changing instances
as necessary, and returns the list of instances.
"""
if not commit:
self.saved_forms = []
def save_m2m():
for form in self.saved_forms:
form.save_m2m()
self.save_m2m = save_m2m
return self.save_existing_objects(commit) + self.save_new_objects(commit)

如您所见,调用save_m2m后,每个内联都保存在循环中。数组按窗体中出现的顺序排序。

不过,这与其说是关于Django管理员,不如说是关于在数据库级别上是否可能的问题。唯一可行的可能性是事务,但即便如此 - 例如postgresql不允许它,除非约束设置为延迟:

https://dba.stackexchange.com/questions/104987/avoid-unique-violation-in-atomic-transaction

Postgresql 文档:https://www.postgresql.org/docs/current/sql-set-constraints.html

因此,要更改行为,您需要:

  • 通过调用原始 SQL 将唯一约束更改为DEFERRED(至少在事务期间)(Django 不支持此功能,请参阅 如何在 django 模型中设置表约束"可延期初始延迟"?
  • 覆盖formset.save以使用transaction.atomic

最新更新