Временное отключение Unique constraint

Имеется модель задачи:

class Task(models.Model):
    index = models.PositiveSmallIntegerField('Нумерация')
    chapter = models.ForeignKey(..., verbose_name='Раздел')
    ...

    class Meta:
        unique_together = ('chapter', 'index')
        verbose_name_plural = 'Задачи'
        verbose_name = 'Задача'

Каждая задача привязана к разделу. Задачи имеют уникальную порядковую нумерацию в рамках раздела.

Мне необходимо реализовать функцию изменения порядка задач, в UI это выглядит как перетаскивание задач путем нажатия на стрелочки вверх/вниз. На сервер приходит json вида:

{
  *task_id1*: *order_delta1*,
  *task_id2*: *order_delta2*,
}

task_id - id задачи order_delta - дельта, на которую поменялась нумерация задачи

И дальше я написал вот такую функцию:

def save_order_changes(changes: list[ChangeEntry]):
    changed_ids = (change.task_id for change in changes)
    # Создаем условия
    statements = (
        When(pk=change.task_id, then=F('index') + change.delta)
        for change in changes
    )
    # Создаем условия
    Task.objects.filter(pk__in=changed_ids).update(
        index=Case(*statements)
    )

Но из-за unique_together я получаю ошибку:

django.db.utils.IntegrityError: UNIQUE constraint failed: tasks_task.chapter_id, tasks_task.index

Мне казалось, что в рамках одного запроса проверка падать не должна, ведь значения в конечном виде должны фиксироваться с соблюдением уникальности?

Есть ли варианты обхода? Можно ли временно отключить для одного запроса проверку на уникальность?

Решил проблему используя фейковые временные номера, чтобы освободить изменяемые значения

Порядок действий такой:

  1. Запоминаем старые номера изменяемых записей
  2. Изменяемым записям проставляем фейковые номера, к примеру 30000, 30001 и т.д
  3. Используя сохраненные старые номера проставляем новые валидные

Некоторые СУБД поддерживают отложенную проверку ограничений. Это конфигурируется так:

from django.db.models import Deferrable, UniqueConstraint

class Task(..):

  class Meta:
    contraints = [
        UniqueConstraint(
            name='unique_chapter_index',
            fields=['chapter', 'index'],
            deferrable=Deferrable.DEFERRED,
        )
    ]

Проверка в таком случае делается в конце транзакции. Если используется автокоммит, то в конце операции update.

Вернуться на верх