Запретить нескольким пользователям бронировать один и тот же столик одновременно в Django

У меня есть модель бронирования, в которой пользователи могут забронировать столик в ресторане на определенную дату и время. Однако я хочу убедиться, что несколько пользователей не смогут забронировать один и тот же столик одновременно. Вот моя модель:

class Booking(TimeStampWithCreatorModel, TerminalMixin, SoftDeletableModel):
    table = models.ManyToManyField(RestaurantTable, related_name="booking_table")
    customer = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name="booking_customer")
    no_of_people = models.IntegerField()
    booking_date = models.DateField()
    booking_time = models.TimeField(null=True, blank=True)
    note = models.TextField(blank=True)
    accept_terms_and_conditions = models.BooleanField(default=False)
    receive_emails = models.BooleanField(default=False)

    class Meta:
        ordering = ("-created",)

Что я пробовал Я пытался использовать select_for_update для блокировки строк при бронировании, но когда несколько пользователей пытаются забронировать один и тот же столик в одно и то же время, в некоторых случаях все равно создаются повторяющиеся заказы.

Вот подход, который я использовал:

from django.db import transaction

def create_booking(customer, table_ids, booking_date, booking_time, no_of_people):
    with transaction.atomic():
        tables = RestaurantTable.objects.filter(id__in=table_ids).select_for_update()

        if Booking.objects.filter(table__in=tables, booking_date=booking_date, booking_time=booking_time).exists():
            raise ValueError("Table already booked for this time slot.")

        booking = Booking.objects.create(
            customer=customer,
            no_of_people=no_of_people,
            booking_date=booking_date,
            booking_time=booking_time
        )
        booking.table.set(tables)

        return booking

Проблема Несмотря на использование select_for_update, когда несколько экземпляров пытаются выполнить бронирование одновременно, проверка Booking.objects.filter(...) не предотвращает условия гонки должным образом. Я предполагаю, что это происходит потому, что ManyToManyField в Django не блокирует отношения так же, как ForeignKey.

Ожидаемый результат Не позволяйте нескольким пользователям бронировать один и тот же столик на одну и ту же дату и время. Убедитесь, что транзакции должным образом обрабатывают параллелизм без условий гонки. Вопросы Как я могу гарантировать, что заказы будут атомарными и предотвратить повторные заказы в один и тот же временной интервал? Есть ли лучший подход, например, ограничения на уровне базы данных или использование выражений F()? Должен ли я реализовать пользовательское ограничение базы данных для обеспечения уникальности таблицы + даты + времени? Буду очень признателен за любые предложения или рекомендации! 🚀

Вы можете использовать сериализуемый уровень изоляции в своих транзакциях. Например, два потока, T1 и T2, пытаются забронировать одну и ту же таблицу одновременно. T2 блокируется, как только он пытается выполнить любую операцию (будь то чтение или запись), которая противоречит текущей транзакции T1.

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