Нарушение уникального ограничения встроенного админа Django при редактировании - PullRequest
0 голосов
/ 07 марта 2019

У меня есть форма со встроенным. Связанная модель имеет уникальное ограничение вместе (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

1 Ответ

1 голос
/ 08 марта 2019

Поскольку вы используете встроенные функции администратора Django, следующее не будет иметь место:

, и я пытаюсь отредактировать этих потомков в одной операции до состояния

Inlines являются частью полной формы, они будут обрабатываться в порядке их появления в форме.Таким образом, даже если вы измените их «в одно действие», то есть вы измените их в течение одной формы POST, Django все равно будет сохранять эти связанные объекты один за другим:

Первый встроенный список сохраняется, ион пытается сохранить parent_id 1 с номером 2, пока в базе данных еще есть parent_id 2 с номером 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, сколько вопрос о том, возможно ли это вообще на уровне базы данных,хотя. Единственная возможность, которая могла бы работать, была бы транзакцией, но даже тогда - postgresql, например, не позволил бы это, если ограничение не установлено в ОТЛИЧНОЕ:

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

Postgresqlдокументация: https://www.postgresql.org/docs/current/sql-set-constraints.html

Итак, чтобы изменить поведение, вам потребуется:

...