diff --git a/.gitignore b/.gitignore index fd1f3e48..9c18d6ac 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,4 @@ venv/ .pytest_cache/ **__pycache__/ *.pyc -app/db.sqlite3 \ No newline at end of file +db.sqlite3 \ No newline at end of file diff --git a/cinema/models.py b/cinema/models.py index f18f166c..2bebd915 100644 --- a/cinema/models.py +++ b/cinema/models.py @@ -28,7 +28,7 @@ class Actor(models.Model): last_name = models.CharField(max_length=255) def __str__(self): - return self.first_name + " " + self.last_name + return f"{self.first_name} {self.last_name}" @property def full_name(self): diff --git a/cinema/serializers.py b/cinema/serializers.py index a1a4d7d4..c16f7636 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,67 @@ 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(read_only=True) + + class Meta: + model = Ticket + fields = ("id", "row", "seat", "movie_session") + + +class TicketTakenPlacesSerializer(serializers.ModelSerializer): + class Meta: + model = Ticket + fields = ("row", "seat") + + 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") + + +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) diff --git a/cinema/urls.py b/cinema/urls.py index e3586f00..5ad6fb5b 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..8cb2314b 100644 --- a/cinema/views.py +++ b/cinema/views.py @@ -1,6 +1,8 @@ +from django.db.models import Count, F from rest_framework import viewsets +from rest_framework.pagination import PageNumberPagination -from cinema.models import Genre, Actor, CinemaHall, Movie, MovieSession +from cinema.models import Genre, Actor, CinemaHall, Movie, MovieSession, Order from cinema.serializers import ( GenreSerializer, @@ -11,7 +13,7 @@ MovieSessionListSerializer, MovieDetailSerializer, MovieSessionDetailSerializer, - MovieListSerializer, + MovieListSerializer, OrderSerializer, OrderListSerializer, ) @@ -43,16 +45,100 @@ def get_serializer_class(self): return MovieSerializer + @staticmethod + def _params_to_int(query_string): + 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 actors: + actors = self._params_to_int(actors) + queryset = queryset.filter(actors__id__in=actors) + + elif genres: + genres = self._params_to_int(genres) + queryset = queryset.filter(genres__id__in=genres) + elif title: + queryset = queryset.filter(title__icontains=title) + return queryset + class MovieSessionViewSet(viewsets.ModelViewSet): queryset = MovieSession.objects.all() serializer_class = MovieSessionSerializer + @staticmethod + def _params_to_int(query_string): + return [int(str_id) for str_id in query_string.split(",")] + def get_serializer_class(self): if self.action == "list": return MovieSessionListSerializer - - if self.action == "retrieve": + elif self.action == "retrieve": return MovieSessionDetailSerializer return MovieSessionSerializer + + def get_queryset(self): + queryset = self.queryset + if self.action in "list": + queryset = queryset.select_related().annotate( + tickets_available=( + F("cinema_hall__rows") + * F("cinema_hall__seats_in_row") + - Count("tickets") + ) + ) + + date = self.request.query_params.get("date") + movie = self.request.query_params.get("movie") + if date and movie: + movie = self._params_to_int(movie) + queryset = queryset.filter( + show_time__date=date, + movie__id__in=movie + ) + elif date: + queryset = queryset.filter( + show_time__date=date + ) + elif movie: + movie = self._params_to_int(movie) + queryset = queryset.filter(movie__id__in=movie) + return queryset + + +class OrderPagination(PageNumberPagination): + page_size = 5 + page_size_query_param = "page_size" + max_page_size = 10 + + +class OrderViewSet(viewsets.ModelViewSet): + serializer_class = OrderSerializer + queryset = Order.objects.all() + pagination_class = OrderPagination + + def get_queryset(self): + queryset = self.queryset.filter(user=self.request.user) + if self.action == "list": + queryset = queryset.prefetch_related( + "tickets__movie_session__cinema_hall", + "tickets__movie_session__movie", + ) + return queryset + + def perform_create(self, serializer): + serializer.save(user=self.request.user) + + def get_serializer_class(self): + serializer = self.serializer_class + + if self.action == "list": + serializer = OrderListSerializer + + return serializer diff --git a/cinema_service/settings.py b/cinema_service/settings.py index a7d6c992..068db3d6 100644 --- a/cinema_service/settings.py +++ b/cinema_service/settings.py @@ -124,7 +124,7 @@ USE_I18N = True -USE_TZ = False +USE_TZ = True # Static files (CSS, JavaScript, Images)