diff --git a/cinema/pagination.py b/cinema/pagination.py new file mode 100644 index 00000000..3ef73431 --- /dev/null +++ b/cinema/pagination.py @@ -0,0 +1,7 @@ +from rest_framework.pagination import PageNumberPagination + + +class CustomPagination(PageNumberPagination): + page_size = 5 + page_size_query_param = "page_size" + max_page_size = 50 diff --git a/cinema/serializers.py b/cinema/serializers.py index a1a4d7d4..ab4f7073 100644 --- a/cinema/serializers.py +++ b/cinema/serializers.py @@ -1,6 +1,15 @@ +from django.db import transaction from rest_framework import serializers -from cinema.models import Genre, Actor, CinemaHall, Movie, MovieSession +from cinema.models import ( + Genre, + Actor, + CinemaHall, + Movie, + MovieSession, + Ticket, + Order +) class GenreSerializer(serializers.ModelSerializer): @@ -59,6 +68,7 @@ class MovieSessionListSerializer(MovieSessionSerializer): cinema_hall_capacity = serializers.IntegerField( source="cinema_hall.capacity", read_only=True ) + tickets_available = serializers.IntegerField(read_only=True) class Meta: model = MovieSession @@ -68,13 +78,73 @@ class Meta: "movie_title", "cinema_hall_name", "cinema_hall_capacity", + "tickets_available" ) +class TicketSerializer(serializers.ModelSerializer): + class Meta: + model = Ticket + fields = ( + "id", + "row", + "seat", + "movie_session" + ) + + +class TicketListSerializer(TicketSerializer): + movie_session = MovieSessionListSerializer(many=False, read_only=True) + + +class TicketTakenPlacesSerializer(TicketSerializer): + class Meta: + model = Ticket + fields = ( + "row", + "seat", + ) + + +class OrderSerializer(serializers.ModelSerializer): + tickets = TicketSerializer(many=True, read_only=False, allow_empty=False) + + class Meta: + model = Order + fields = ("id", "tickets", "created_at") + + def create(self, validated_data): + with transaction.atomic(): + tickets_data = validated_data.pop("tickets") + order = Order.objects.create(**validated_data) + for ticket_data in tickets_data: + Ticket.objects.create(order=order, **ticket_data) + return order + + +class OrderListSerializer(OrderSerializer): + tickets = TicketListSerializer(many=True, read_only=True) + + +class OrderDetailSerializer(OrderSerializer): + tickets = TicketListSerializer(many=True, read_only=True) + + class MovieSessionDetailSerializer(MovieSessionSerializer): movie = MovieListSerializer(many=False, read_only=True) cinema_hall = CinemaHallSerializer(many=False, read_only=True) + taken_places = TicketTakenPlacesSerializer( + many=True, + read_only=True, + source="tickets" + ) class Meta: model = MovieSession - fields = ("id", "show_time", "movie", "cinema_hall") + fields = ( + "id", + "show_time", + "movie", + "cinema_hall", + "taken_places" + ) diff --git a/cinema/urls.py b/cinema/urls.py index e3586f00..754f7330 100644 --- a/cinema/urls.py +++ b/cinema/urls.py @@ -7,6 +7,7 @@ CinemaHallViewSet, MovieViewSet, MovieSessionViewSet, + OrderViewSet ) router = routers.DefaultRouter() @@ -15,6 +16,7 @@ router.register("cinema_halls", CinemaHallViewSet) router.register("movies", MovieViewSet) router.register("movie_sessions", MovieSessionViewSet) +router.register("orders", OrderViewSet) urlpatterns = [path("", include(router.urls))] diff --git a/cinema/views.py b/cinema/views.py index c4ff85e9..9d82bd75 100644 --- a/cinema/views.py +++ b/cinema/views.py @@ -1,6 +1,15 @@ +from django.db.models import Count, F + from rest_framework import viewsets -from cinema.models import Genre, Actor, CinemaHall, Movie, MovieSession +from cinema.models import ( + Genre, + Actor, + CinemaHall, + Movie, + MovieSession, + Order +) from cinema.serializers import ( GenreSerializer, @@ -12,8 +21,13 @@ MovieDetailSerializer, MovieSessionDetailSerializer, MovieListSerializer, + OrderSerializer, + OrderListSerializer, + OrderDetailSerializer ) +from .pagination import CustomPagination + class GenreViewSet(viewsets.ModelViewSet): queryset = Genre.objects.all() @@ -31,9 +45,33 @@ class CinemaHallViewSet(viewsets.ModelViewSet): class MovieViewSet(viewsets.ModelViewSet): - queryset = Movie.objects.all() + queryset = Movie.objects.prefetch_related("actors", "genres") serializer_class = MovieSerializer + @staticmethod + def params_to_ints(query_string: str) -> list[int]: + return [int(str_id) for str_id in query_string.split(",")] + + def get_queryset(self): + queryset = self.queryset + + actors = self.request.query_params.get("actors") + genres = self.request.query_params.get("genres") + title = self.request.query_params.get("title") + + if title: + queryset = queryset.filter(title__icontains=title) + + if genres: + genres = self.params_to_ints(genres) + queryset = queryset.filter(genres__id__in=genres) + + if actors: + actors = self.params_to_ints(actors) + queryset = queryset.filter(actors__id__in=actors) + + return queryset.distinct() + def get_serializer_class(self): if self.action == "list": return MovieListSerializer @@ -48,6 +86,30 @@ class MovieSessionViewSet(viewsets.ModelViewSet): queryset = MovieSession.objects.all() serializer_class = MovieSessionSerializer + def get_queryset(self): + queryset = self.queryset + + if self.action == "list": + queryset = ( + queryset + .annotate( + tickets_available=( + F("cinema_hall__rows") * F("cinema_hall__seats_in_row") + - Count("tickets") + ) + ) + ).order_by("show_time") + + movie = self.request.query_params.get("movie") + date = self.request.query_params.get("date") + + if movie: + queryset = queryset.filter(movie__id=int(movie)) + if date: + queryset = queryset.filter(show_time__date=date) + + return queryset.select_related("cinema_hall", "movie") + def get_serializer_class(self): if self.action == "list": return MovieSessionListSerializer @@ -56,3 +118,27 @@ def get_serializer_class(self): return MovieSessionDetailSerializer return MovieSessionSerializer + + +class OrderViewSet(viewsets.ModelViewSet): + queryset = Order.objects + pagination_class = CustomPagination + + def get_queryset(self): + queryset = Order.objects.prefetch_related( + "tickets__movie_session__movie", + "tickets__movie_session__cinema_hall" + ).filter( + user=self.request.user + ) + return queryset + + def perform_create(self, serializer): + serializer.save(user=self.request.user) + + def get_serializer_class(self): + if self.action == "list": + return OrderListSerializer + if self.action == "retrieve": + return OrderDetailSerializer + return OrderSerializer