I have a ticket model, and its ticket serialiazer. The ticket model has a bought
and a booked_at
field. And also a unique_together
attribute for show and seat.
class Ticket(models.Model):
show = models.ForeignKey(Show, on_delete=models.CASCADE)
seat = models.ForeignKey(Seat, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
booked_at = models.DateTimeField(default=timezone.now)
bought = models.BooleanField(default=False)
class Meta:
unique_together = ('show', 'seat')
- On the ticket serializer, the serializer on validation checks whether there is any ticket with the required seat and show
- If there is a ticket then it checks whether the ticket was bought or not.
- If it is bought, then it will raise an error.
- If its not bought then Check if the ticket was booked within 5 minutes.
- If its booked within 5 minutes, then raise an error.
- Else if the booked time is more than 5 minutes, then delete the old ticket and return valid.
- If there is a ticket then it checks whether the ticket was bought or not.
- If there is not ticket then return valid
TicketSerializer:
class TicketSerializer(serializers.Serializer):
seat = serializers.PrimaryKeyRelatedField(queryset=Seat.objects.all())
show = serializers.PrimaryKeyRelatedField(queryset=Show.objects.all())
user = serializers.PrimaryKeyRelatedField(queryset=User.objects.all())
bought = serializers.BooleanField(default=False)
def validate(self, attrs):
if attrs['seat']:
try:
ticket = Ticket.objects.get(show=attrs['show'], seat=seat)
if not ticket.bought:
if ticket.booked_at < timezone.now() - datetime.timedelta(minutes=5):
# ticket booked crossed the deadline
ticket.delete()
return attrs
else:
# ticket in 5 mins range
raise serializers.ValidationError("Ticket with same show and seat exists.")
else:
raise serializers.ValidationError("Ticket with same show and seat exists.")
except Ticket.DoesNotExist:
return attrs
else:
raise serializers.ValidationError("No seat value provided.")
On the view, I am using @transaction.atomic()
to make sure the ticket/s are created only if all of them are valid, or don't create ANY ticket if not valid.
@transaction.atomic()
@list_route(
methods=['POST'],
permission_classes=[IsAuthenticated],
url_path='book-tickets-by-show/(?P<show_id>[0-9]+)'
)
def book_tickets_by_show(self, request, show_id=None):
try:
show = Show.objects.get(id=show_id)
user = request.user
...
...
data_list = [...]
with transaction.atomic():
try:
serializer = TicketSerializer(data=data_list, many=True)
if serializer.is_valid():
serializer.save()
....
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
except (Seat.DoesNotExist, ValueError, ConnectionError) as e:
return Response({'detail': str(e)}, status=status.HTTP_400_BAD_REQUEST)
except (Show.DoesNotExist, IntegrityError) as e:
return Response({'detail': str(e)}, status=status.HTTP_400_BAD_REQUEST)
What I would like to know is will it help in preventing when more than one requests are called for creating the ticket/s for same seat/s?
Suppose, User A wants to book ticket for seats 5,6. User B wants to book ticket for seats 3,6, and another User C wants to book the ticket for seats 2,3,4,5,6.
Will the above method prevent booking tickets for their respective seats for all the users, and only create tickets for one user (maybe whose transaction was first)? Or if there is a better way then could you please tell me how to. I hope I was clear. If not please ask.