Запретить нескольким пользователям бронировать один и тот же столик одновременно в 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.