【问题标题】:Django admin inline unique constraint violation on edit编辑时 Django 管理员内联唯一约束违规
【发布时间】:2019-03-07 13:19:21
【问题描述】:

我有一个内联表单。相关模型具有唯一的共同约束(parent_id, number)。我有一个有两个孩子的实体

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

【问题讨论】:

  • 这是 parent_id 主键吗?
  • 您能否将您的代码粘贴到“我正在尝试通过一项操作将这些子项编辑到该州” - 您是否将queryset.updateF() 结合使用?显然,现在,您的代码发出后续语句并且数据库正确拒绝它。
  • @NVS 用于父表,是的。对于子表,没有。
  • @Risadinha 我不明白你在说什么。我相信 django-admin 生成的更新操作。我没有为此做任何特殊的代码,只是定义了模型、管理表单和内联。

标签: django django-admin


【解决方案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),代码如下。

注意:这里,“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 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

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

【讨论】:

  • 感谢您的回答。但是,如果我理解正确,如果我按倒序更改子记录(例如 [(parent_id, 2), (parent_id, 1)],变成 [(parent_id, 3), (parent_id, 2)]),它会起作用。有趣的是它没有,所以我认为真正的问题是完全不同的。
  • 仅当您在两者之间保存时。如果您以任何顺序更改数字并且之后只保存一次,那么唯一重要的顺序是内联在表单中出现的顺序(在页面上)。
  • @Risadina 我让它们以正确的顺序出现。是 (parent_id, 2), (parent_id, 1) 我做了 (parent_id, 3), (parent_id, 2)。如果你是正确的,那么首先将被保存 (parent_id, 3) 并且在 (parent_id, 2) 被保存时没有其他记录 (parent_id, 2) 所以它应该可以正常工作。但事实并非如此。我还深入研究了代码,它在 Django 验证级别失败,甚至没有尝试保存对象。
猜你喜欢
  • 1970-01-01
  • 2018-09-06
  • 1970-01-01
  • 1970-01-01
  • 2022-01-23
  • 2016-06-05
  • 2022-09-23
  • 2012-10-10
  • 1970-01-01
相关资源
最近更新 更多