Как обрабатывать django.db.utils.IntegrityError для асинхронных POST? (Можете ли вы исключить IntegrityError?)
У меня есть модель с сигнальным хуком @BEFORE_CREATE следующим образом:
from django.lifecycle.hooks import BEFORE_CREATE
class Item(models.Model):
title = models.CharField()
slug = models.JSONField(unique=True)
@hook(BEFORE_CREATE)
def populate_slug(self):
# Create a slug from the title
# If the slug already exists, append "-{n}"
# to make sure it is unique
slug[0] = slugify(self.title)
n = 2
while List.objects.filter(slug=slug).exists():
slug[0] = f"{slug[0]-{n}"
n += 1
self.slug = slug
Это отлично работает в моих тестах, когда элементы создаются синхронно один за другим:
i1 = Item.objects.create(title="Hello")
i2 = Item.objects.create(title="Hello")
self.assertEqual(i1.slug, ["hello"])
self.assertEqual(i2.slug, ["hello-2"])
Однако на моем реальном фронтенде я создаю эти объекты асинхронно и в одно и то же время:
const item1 = axios.post(POST_ITEMS_URL, {title: "Hello"})
const item2 = axios.post(POST_ITEMS_URL, {title: "Hello"})
Promise.all([item1, item2]);
Что дает мне эту ошибку:
web_1 | django.db.utils.IntegrityError: duplicate key value violates unique constraint "app_item_slug_key"
web_1 | DETAIL: Key (slug)=(["hello"]) already exists.
Простым исправлением (я думаю) будет изменение моего @hook на AFTER_CREATE, но я хотел бы избежать дополнительной записи для всех объектов, если это возможно.
Можно ли куда-нибудь поместить except IntegrityError? Если да, то куда?
Или как еще я могу решить эту проблему?
Я использую PostGRE, если это имеет значение.
Выяснили, куда поместить IntegrityError - путем перезаписи метода create представления Item:
# To retry 3 times
def create(self, request, *args, **kwargs):
n = 0
while n < 4:
try:
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(
serializer.data, status=status.HTTP_201_CREATED, headers=headers
)
except IntegrityError:
n += 1
continue
break
Async POSTs на фронтенде теперь работают :)