我有一个内联表格。相关模型具有唯一的共同约束(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 |
在两个单独的操作中,效果很好。
ParentAdmin定义
class ParentAdmin(admin.ModelAdmin):
form = BaseForm
inlines = [LevelExerciseInline]
ChildInline定义
class ChildInline(admin.StackedInline):
form = BaseForm
model = Child
extra = 3
答案 0 :(得分:1)
在使用Django Admin内联时,情况并非如此:
我正在尝试通过一次操作将这些子级编辑为状态
内联是完整表格的一部分,将按照它们在表格中出现的顺序进行处理。因此,即使您“一次动作”更改它们,这意味着您在一种形式的POST期间更改它们,Django仍将一一保存这些相关对象:
列表中的第一个内联被保存,并且它尝试保存编号为2的parent_id 1,而数据库中仍然存在编号为2的parent_id 2。
具体地说,使用以下代码调用了formset.save()
(https://github.com/django/django/blob/master/django/forms/models.py#L655)。
注意:这里,“表单”是实际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 Admin无关,而更多的是关于它是否甚至可以在数据库级别上的问题。唯一可行的可能是事务,但即使如此-例如postgresql也不允许它,除非将约束设置为DEFERRED:
https://dba.stackexchange.com/questions/104987/avoid-unique-violation-in-atomic-transaction
Postgresql文档:https://www.postgresql.org/docs/current/sql-set-constraints.html
因此,要更改行为,您需要:
DEFERRED
(至少在事务期间)(Django不支持此约束,请参见How can I set a table constraint "deferrable initially deferred" in django model?)formset.save
以使用transaction.atomic