Временное отключение 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
Мне казалось, что в рамках одного запроса проверка падать не должна, ведь значения в конечном виде должны фиксироваться с соблюдением уникальности?
Есть ли варианты обхода? Можно ли временно отключить для одного запроса проверку на уникальность?
Решил проблему используя фейковые временные номера, чтобы освободить изменяемые значения
Порядок действий такой:
- Запоминаем старые номера изменяемых записей
- Изменяемым записям проставляем фейковые номера, к примеру 30000, 30001 и т.д
- Используя сохраненные старые номера проставляем новые валидные
Некоторые СУБД поддерживают отложенную проверку ограничений. Это конфигурируется так:
from django.db.models import Deferrable, UniqueConstraint
class Task(..):
class Meta:
contraints = [
UniqueConstraint(
name='unique_chapter_index',
fields=['chapter', 'index'],
deferrable=Deferrable.DEFERRED,
)
]
Проверка в таком случае делается в конце транзакции. Если используется автокоммит, то в конце операции update.