From a68407ffbe440780dad94c0b1fb045fa315e6d97 Mon Sep 17 00:00:00 2001 From: AndreiVed Date: Tue, 15 Oct 2024 20:34:40 +0300 Subject: [PATCH 1/3] solution --- .gitignore | 2 +- cinema/models.py | 2 +- cinema/serializers.py | 60 +++++++++++++++++++++++++- cinema/urls.py | 2 + cinema/views.py | 86 ++++++++++++++++++++++++++++++++++++-- cinema_service/settings.py | 7 +++- 6 files changed, 150 insertions(+), 9 deletions(-) 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..9170739d 100644 --- a/cinema/serializers.py +++ b/cinema/serializers.py @@ -1,6 +1,7 @@ +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 +60,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 +70,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..07dd1f26 100644 --- a/cinema/views.py +++ b/cinema/views.py @@ -1,6 +1,7 @@ +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, @@ -11,7 +12,7 @@ MovieSessionListSerializer, MovieDetailSerializer, MovieSessionDetailSerializer, - MovieListSerializer, + MovieListSerializer, OrderSerializer, OrderListSerializer, ) @@ -43,16 +44,93 @@ 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__contains=date, + movie__id__in=movie + ) + elif date: + queryset = queryset.filter( + show_time__contains=date + ) + elif movie: + movie = self._params_to_int(movie) + queryset = queryset.filter(movie__id__in=movie) + return queryset + + +class OrderViewSet(viewsets.ModelViewSet): + serializer_class = OrderSerializer + queryset = Order.objects.all() + + 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..cc52b15c 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) @@ -136,3 +136,8 @@ # https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" + +REST_FRAMEWORK = { + 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', + 'PAGE_SIZE': 3 +} From 2577f2afe462025041bdbc0c9d1f48f744ca82a5 Mon Sep 17 00:00:00 2001 From: AndreiVed Date: Tue, 15 Oct 2024 23:02:44 +0300 Subject: [PATCH 2/3] solution --- cinema/views.py | 12 ++++++++++-- cinema_service/settings.py | 8 ++++---- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/cinema/views.py b/cinema/views.py index 07dd1f26..8cb2314b 100644 --- a/cinema/views.py +++ b/cinema/views.py @@ -1,5 +1,6 @@ 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, Order @@ -98,12 +99,12 @@ def get_queryset(self): if date and movie: movie = self._params_to_int(movie) queryset = queryset.filter( - show_time__contains=date, + show_time__date=date, movie__id__in=movie ) elif date: queryset = queryset.filter( - show_time__contains=date + show_time__date=date ) elif movie: movie = self._params_to_int(movie) @@ -111,9 +112,16 @@ def get_queryset(self): 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) diff --git a/cinema_service/settings.py b/cinema_service/settings.py index cc52b15c..022aa33b 100644 --- a/cinema_service/settings.py +++ b/cinema_service/settings.py @@ -137,7 +137,7 @@ DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" -REST_FRAMEWORK = { - 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', - 'PAGE_SIZE': 3 -} +# REST_FRAMEWORK = { +# 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', +# 'PAGE_SIZE': 3 +# } From 8670ae7330d375cbab952313b31a68713d1e4833 Mon Sep 17 00:00:00 2001 From: AndreiVed Date: Tue, 15 Oct 2024 23:04:28 +0300 Subject: [PATCH 3/3] solution --- cinema/serializers.py | 10 +++++++++- cinema_service/settings.py | 5 ----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/cinema/serializers.py b/cinema/serializers.py index 9170739d..c16f7636 100644 --- a/cinema/serializers.py +++ b/cinema/serializers.py @@ -1,7 +1,15 @@ from django.db import transaction from rest_framework import serializers -from cinema.models import Genre, Actor, CinemaHall, Movie, MovieSession, Ticket, Order +from cinema.models import ( + Genre, + Actor, + CinemaHall, + Movie, + MovieSession, + Ticket, + Order +) class GenreSerializer(serializers.ModelSerializer): diff --git a/cinema_service/settings.py b/cinema_service/settings.py index 022aa33b..068db3d6 100644 --- a/cinema_service/settings.py +++ b/cinema_service/settings.py @@ -136,8 +136,3 @@ # https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" - -# REST_FRAMEWORK = { -# 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', -# 'PAGE_SIZE': 3 -# }