From 8d777cc77e47d4e792767905561b45443b585e70 Mon Sep 17 00:00:00 2001 From: Olha Stadnik Date: Sat, 14 Dec 2024 21:37:36 +0200 Subject: [PATCH 1/2] "solution" --- cinema/migrations/0002_initial.py | 6 +- cinema/migrations/0003_movie_duration.py | 6 +- cinema/migrations/0004_alter_genre_name.py | 6 +- cinema/serializers.py | 58 +++++++++++++++- cinema/tests/test_movie_session_api.py | 7 +- cinema/urls.py | 4 ++ cinema/views.py | 80 +++++++++++++++++++++- cinema_service/settings.py | 13 ++-- 8 files changed, 153 insertions(+), 27 deletions(-) diff --git a/cinema/migrations/0002_initial.py b/cinema/migrations/0002_initial.py index e6341fe7..37569233 100644 --- a/cinema/migrations/0002_initial.py +++ b/cinema/migrations/0002_initial.py @@ -19,14 +19,16 @@ class Migration(migrations.Migration): model_name="order", name="user", field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, ), ), migrations.AddField( model_name="moviesession", name="cinema_hall", field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to="cinema.cinemahall" + on_delete=django.db.models.deletion.CASCADE, + to="cinema.cinemahall", ), ), migrations.AddField( diff --git a/cinema/migrations/0003_movie_duration.py b/cinema/migrations/0003_movie_duration.py index 7355c91a..e75f6794 100644 --- a/cinema/migrations/0003_movie_duration.py +++ b/cinema/migrations/0003_movie_duration.py @@ -6,13 +6,13 @@ class Migration(migrations.Migration): dependencies = [ - ('cinema', '0002_initial'), + ("cinema", "0002_initial"), ] operations = [ migrations.AddField( - model_name='movie', - name='duration', + model_name="movie", + name="duration", field=models.IntegerField(default=123), preserve_default=False, ), diff --git a/cinema/migrations/0004_alter_genre_name.py b/cinema/migrations/0004_alter_genre_name.py index 83f65fd1..eb3d0ffb 100644 --- a/cinema/migrations/0004_alter_genre_name.py +++ b/cinema/migrations/0004_alter_genre_name.py @@ -6,13 +6,13 @@ class Migration(migrations.Migration): dependencies = [ - ('cinema', '0003_movie_duration'), + ("cinema", "0003_movie_duration"), ] operations = [ migrations.AlterField( - model_name='genre', - name='name', + model_name="genre", + name="name", field=models.CharField(max_length=255, unique=True), ), ] diff --git a/cinema/serializers.py b/cinema/serializers.py index a1a4d7d4..3160e2a8 100644 --- a/cinema/serializers.py +++ b/cinema/serializers.py @@ -1,6 +1,14 @@ from rest_framework import serializers -from cinema.models import Genre, Actor, CinemaHall, Movie, MovieSession +from cinema.models import ( + Genre, + Actor, + CinemaHall, + Movie, + MovieSession, + Order, + Ticket, +) class GenreSerializer(serializers.ModelSerializer): @@ -59,6 +67,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 +77,58 @@ class Meta: "movie_title", "cinema_hall_name", "cinema_hall_capacity", + "tickets_available", ) +class TicketSeatRowSerializer(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 = TicketSeatRowSerializer( + 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 TicketSerializer(serializers.ModelSerializer): + movie_session = MovieSessionListSerializer(many=False, read_only=True) + + class Meta: + model = Ticket + fields = ("id", "movie_session", "row", "seat") + + def validate(self, data): + data = super(TicketSerializer).validate(data) + ticket = Ticket( + movie_session=data.get("movie_session"), + row=data.get("row"), + seat=data.get("seat"), + ) + ticket.full_clean() + return data + + +class OrderSerializer(serializers.ModelSerializer): + tickets = TicketSerializer(many=True, read_only=True, allow_null=False) + + class Meta: + model = Order + fields = ("id", "created_at", "tickets") + + def create(self, validated_data): + 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 diff --git a/cinema/tests/test_movie_session_api.py b/cinema/tests/test_movie_session_api.py index 9b75d7ed..78ff2db6 100644 --- a/cinema/tests/test_movie_session_api.py +++ b/cinema/tests/test_movie_session_api.py @@ -34,12 +34,7 @@ def setUp(self): self.movie_session = MovieSession.objects.create( movie=self.movie, cinema_hall=self.cinema_hall, - show_time=datetime.datetime( - year=2022, - month=9, - day=2, - hour=9 - ), + show_time=datetime.datetime(year=2022, month=9, day=2, hour=9), ) def test_get_movie_sessions(self): diff --git a/cinema/urls.py b/cinema/urls.py index e3586f00..50920ad8 100644 --- a/cinema/urls.py +++ b/cinema/urls.py @@ -7,6 +7,8 @@ CinemaHallViewSet, MovieViewSet, MovieSessionViewSet, + OrderViewSet, + TicketViewSet, ) router = routers.DefaultRouter() @@ -15,6 +17,8 @@ router.register("cinema_halls", CinemaHallViewSet) router.register("movies", MovieViewSet) router.register("movie_sessions", MovieSessionViewSet) +router.register("orders", OrderViewSet) +router.register("tickets", TicketViewSet) urlpatterns = [path("", include(router.urls))] diff --git a/cinema/views.py b/cinema/views.py index c4ff85e9..449a76b7 100644 --- a/cinema/views.py +++ b/cinema/views.py @@ -1,6 +1,16 @@ +from django.db.models import Count, F from rest_framework import viewsets - -from cinema.models import Genre, Actor, CinemaHall, Movie, MovieSession +from rest_framework.pagination import PageNumberPagination + +from cinema.models import ( + Genre, + Actor, + CinemaHall, + Movie, + MovieSession, + Order, + Ticket, +) from cinema.serializers import ( GenreSerializer, @@ -12,6 +22,8 @@ MovieDetailSerializer, MovieSessionDetailSerializer, MovieListSerializer, + OrderSerializer, + TicketSerializer, ) @@ -43,6 +55,28 @@ def get_serializer_class(self): return MovieSerializer + def get_queryset(self): + queryset = super().get_queryset() + if self.action == ("list", "retrieve"): + queryset = self.queryset.prefetch_related("actors", "genres") + + actors = self.request.query_params.get("actors") + genres = self.request.query_params.get("genres") + title = self.request.query_params.get("title") + + if actors: + actors_ids = [int(str_id) for str_id in actors.split(",")] + queryset = queryset.filter(actors__id__in=actors_ids) + + if genres: + genres_ids = [int(str_id) for str_id in genres.split(",")] + queryset = queryset.filter(genres__id__in=genres_ids) + + if title: + queryset = queryset.filter(title__icontains=title) + + return queryset.distinct() + class MovieSessionViewSet(viewsets.ModelViewSet): queryset = MovieSession.objects.all() @@ -56,3 +90,45 @@ def get_serializer_class(self): return MovieSessionDetailSerializer return MovieSessionSerializer + + def get_queryset(self): + queryset = super().get_queryset() + + if self.action == "retrieve": + queryset = queryset.select_related("movie") + + elif self.action == "list": + queryset = ( + queryset.select_related("cinema_hall").annotate( + tickets_available=( + F("cinema_hall__rows") * F("cinema_hall__seats_in_row") + - Count("tickets") + ) + ) + ).order_by("id") + + movie = self.request.query_params.get("movie") + date = self.request.query_params.get("date") + + if movie: + queryset = queryset.filter(movie__id=movie) + + if date: + queryset = queryset.filter(show_time__date=date) + + return queryset.distinct() + + +class TicketViewSet(viewsets.ModelViewSet): + queryset = Ticket.objects.all() + serializer_class = TicketSerializer + + +class OrderPagination(PageNumberPagination): + page_size = 5 + + +class OrderViewSet(viewsets.ModelViewSet): + queryset = Order.objects.all() + serializer_class = OrderSerializer + pagination_class = OrderPagination diff --git a/cinema_service/settings.py b/cinema_service/settings.py index a7d6c992..43042df3 100644 --- a/cinema_service/settings.py +++ b/cinema_service/settings.py @@ -20,9 +20,7 @@ # See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = ( - "django-insecure-6vubhk2$++agnctay_4pxy_8cq)mosmn(*-#2b^v4cgsh-^!i3" -) +SECRET_KEY = "django-insecure-6vubhk2$++agnctay_4pxy_8cq)mosmn(*-#2b^v4cgsh-^!i3" # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True @@ -100,16 +98,13 @@ "UserAttributeSimilarityValidator", }, { - "NAME": "django.contrib.auth.password_validation." - "MinimumLengthValidator", + "NAME": "django.contrib.auth.password_validation." "MinimumLengthValidator", }, { - "NAME": "django.contrib.auth.password_validation." - "CommonPasswordValidator", + "NAME": "django.contrib.auth.password_validation." "CommonPasswordValidator", }, { - "NAME": "django.contrib.auth.password_validation." - "NumericPasswordValidator", + "NAME": "django.contrib.auth.password_validation." "NumericPasswordValidator", }, ] From ced4e031b8170bf6c23e72324d85c1cf3ae78f90 Mon Sep 17 00:00:00 2001 From: Olha Stadnik Date: Sat, 14 Dec 2024 21:41:19 +0200 Subject: [PATCH 2/2] "solution_2" --- .flake8 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.flake8 b/.flake8 index b5381af3..72d6789f 100644 --- a/.flake8 +++ b/.flake8 @@ -7,4 +7,5 @@ select = B,C,E,F,W,T4,B9,Q0,N8,VNE exclude = **migrations venv - tests \ No newline at end of file + tests + cinema_service \ No newline at end of file