Skip to content

Commit

Permalink
Solution
Browse files Browse the repository at this point in the history
  • Loading branch information
zakotii committed Jan 8, 2025
1 parent efd4b75 commit f91b4d9
Show file tree
Hide file tree
Showing 9 changed files with 220 additions and 31 deletions.
44 changes: 44 additions & 0 deletions cinema/filters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from django.db.models import Q
from django.db.models import OuterRef, Subquery
from django_filters import rest_framework as filters
from cinema.models import MovieSession, Movie, Actor


class MovieSessionFilter(filters.FilterSet):
date = filters.DateFilter(field_name="show_time", lookup_expr="date")
movie = filters.NumberFilter(field_name="movie__id")

class Meta:
model = MovieSession
fields = ["date", "movie"]


class MovieFilter(filters.FilterSet):
genres = filters.CharFilter(method="filter_by_genre_ids")
title = filters.CharFilter(field_name="title", lookup_expr="icontains")
actors = filters.CharFilter(method="filter_by_actor_ids")

class Meta:
model = Movie
fields = ["genres", "title", "actors"]

def filter_by_genre_ids(self, queryset, name, value):
genre_ids = [
int(id.strip())
for id in value.split(",")
if id.strip().isdigit()
]
return queryset.filter(genres__id__in=genre_ids)

def filter_by_actor_ids(self, queryset, name, value):
try:
# Преобразуем значения в список ID
actor_ids = [
int(id.strip())
for id in value.split(",")
if id.strip().isdigit()
]
return queryset.filter(actors__id__in=actor_ids)
except ValueError:
# Если передан некорректный ID
return queryset.none()
17 changes: 17 additions & 0 deletions cinema/migrations/0005_alter_actor_options.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 4.1.13 on 2025-01-07 08:21

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
("cinema", "0004_alter_genre_name"),
]

operations = [
migrations.AlterModelOptions(
name="actor",
options={"ordering": ["id"]},
),
]
18 changes: 18 additions & 0 deletions cinema/migrations/0006_alter_movie_actors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.1.4 on 2025-01-07 13:28

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("cinema", "0005_alter_actor_options"),
]

operations = [
migrations.AlterField(
model_name="movie",
name="actors",
field=models.ManyToManyField(related_name="movies", to="cinema.actor"),
),
]
5 changes: 4 additions & 1 deletion cinema/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ class Actor(models.Model):
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)

class Meta:
ordering = ["id"]

def __str__(self):
return self.first_name + " " + self.last_name

Expand All @@ -40,7 +43,7 @@ class Movie(models.Model):
description = models.TextField()
duration = models.IntegerField()
genres = models.ManyToManyField(Genre)
actors = models.ManyToManyField(Actor)
actors = models.ManyToManyField(Actor, related_name="movies")

class Meta:
ordering = ["title"]
Expand Down
118 changes: 96 additions & 22 deletions cinema/serializers.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,74 @@
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

class MovieSessionListSerializer(serializers.ModelSerializer):
tickets_available = serializers.SerializerMethodField()

def get_tickets_available(self, obj):
total_tickets = obj.cinema_hall.capacity
sold_tickets = obj.tickets.count()
return total_tickets - sold_tickets

movie_title = serializers.CharField(source="movie.title", read_only=True)
cinema_hall_name = serializers.CharField(
source="cinema_hall.name", read_only=True
)
cinema_hall_capacity = serializers.IntegerField(
source="cinema_hall.capacity", read_only=True
)

class Meta:
model = MovieSession
fields = (
"id",
"show_time",
"movie_title",
"cinema_hall_name",
"cinema_hall_capacity",
"tickets_available",
)


class TicketSerializer(serializers.ModelSerializer):
movie_session = MovieSessionListSerializer(read_only=True)

class Meta:
model = Ticket
fields = ("id", "row", "seat", "movie_session")

def validate(self, data):
movie_session = data["movie_session"]
row = data["row"]
seat = data["seat"]
if Ticket.objects.filter(
movie_session=movie_session, row=row, seat=seat
).exists():
raise serializers.ValidationError("Это место уже занято.")
if not (1 <= row <= movie_session.cinema_hall.rows):
raise serializers.ValidationError("Недопустимый номер ряда.")
if not (1 <= seat <= movie_session.cinema_hалл.seats_in_row):
raise serializers.ValidationError("Недопустимый номер места.")
return data


class OrderSerializer(serializers.ModelSerializer):
tickets = TicketSerializer(many=True)

class Meta:
model = Order
fields = ("id", "tickets", "created_at")

def create(self, validated_data):
tickets_data = validated_data.pop("tickets")
order = Order.objects.create(user=self.context["request"].user)
for ticket_data in tickets_data:
Ticket.objects.create(order=order, **ticket_data)
return order


class GenreSerializer(serializers.ModelSerializer):
Expand All @@ -10,10 +78,15 @@ class Meta:


class ActorSerializer(serializers.ModelSerializer):
full_name = serializers.SerializerMethodField()

class Meta:
model = Actor
fields = ("id", "first_name", "last_name", "full_name")

def get_full_name(self, obj):
return f"{obj.first_name} {obj.last_name}"


class CinemaHallSerializer(serializers.ModelSerializer):
class Meta:
Expand All @@ -22,10 +95,26 @@ class Meta:


class MovieSerializer(serializers.ModelSerializer):
genres = serializers.PrimaryKeyRelatedField(
many=True, queryset=Genre.objects.all()
)
actors = serializers.PrimaryKeyRelatedField(
many=True, queryset=Actor.objects.all()
)

class Meta:
model = Movie
fields = ("id", "title", "description", "duration", "genres", "actors")

def get_genres(self, obj):
return [genre.name for genre in obj.genres.all()]

def get_actors(self, obj):
return [
f"{actor.first_name} {actor.last_name}"
for actor in obj.actors.all()
]


class MovieListSerializer(MovieSerializer):
genres = serializers.SlugRelatedField(
Expand All @@ -51,30 +140,15 @@ class Meta:
fields = ("id", "show_time", "movie", "cinema_hall")


class MovieSessionListSerializer(MovieSessionSerializer):
movie_title = serializers.CharField(source="movie.title", read_only=True)
cinema_hall_name = serializers.CharField(
source="cinema_hall.name", read_only=True
)
cinema_hall_capacity = serializers.IntegerField(
source="cinema_hall.capacity", read_only=True
)

class Meta:
model = MovieSession
fields = (
"id",
"show_time",
"movie_title",
"cinema_hall_name",
"cinema_hall_capacity",
)


class MovieSessionDetailSerializer(MovieSessionSerializer):
movie = MovieListSerializer(many=False, read_only=True)
cinema_hall = CinemaHallSerializer(many=False, read_only=True)
taken_places = serializers.SerializerMethodField()

class Meta:
model = MovieSession
fields = ("id", "show_time", "movie", "cinema_hall")
fields = ("id", "show_time", "movie", "cinema_hall", "taken_places")

def get_taken_places(self, obj):
tickets = obj.tickets.all()
return [{"row": ticket.row, "seat": ticket.seat} for ticket in tickets]
5 changes: 5 additions & 0 deletions cinema/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,17 @@
MovieSessionViewSet,
)

from cinema.views import OrderViewSet


router = routers.DefaultRouter()
router.register("genres", GenreViewSet)
router.register("actors", ActorViewSet)
router.register("cinema_halls", CinemaHallViewSet)
router.register("movies", MovieViewSet)
router.register("movie_sessions", MovieSessionViewSet)
router.register("orders", OrderViewSet)


urlpatterns = [path("", include(router.urls))]

Expand Down
32 changes: 24 additions & 8 deletions cinema/views.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from rest_framework import viewsets

from cinema.models import Genre, Actor, CinemaHall, Movie, MovieSession

from rest_framework import viewsets, permissions, filters
from django_filters.rest_framework import DjangoFilterBackend
from cinema.models import Genre, Actor, CinemaHall, Movie, MovieSession, Order
from cinema.serializers import (
GenreSerializer,
ActorSerializer,
Expand All @@ -12,7 +11,23 @@
MovieDetailSerializer,
MovieSessionDetailSerializer,
MovieListSerializer,
OrderSerializer,
)
from cinema.filters import MovieFilter, MovieSessionFilter
from rest_framework.response import Response
from rest_framework.pagination import PageNumberPagination

from rest_framework.viewsets import ModelViewSet


class OrderPagination(PageNumberPagination):
page_size = 10


class OrderViewSet(ModelViewSet):
queryset = Order.objects.prefetch_related("tickets").all()
serializer_class = OrderSerializer
pagination_class = OrderPagination


class GenreViewSet(viewsets.ModelViewSet):
Expand All @@ -33,26 +48,27 @@ class CinemaHallViewSet(viewsets.ModelViewSet):
class MovieViewSet(viewsets.ModelViewSet):
queryset = Movie.objects.all()
serializer_class = MovieSerializer
filter_backends = [DjangoFilterBackend, filters.SearchFilter]
filterset_class = MovieFilter
search_fields = ["title"]

def get_serializer_class(self):
if self.action == "list":
return MovieListSerializer

if self.action == "retrieve":
return MovieDetailSerializer

return MovieSerializer


class MovieSessionViewSet(viewsets.ModelViewSet):
queryset = MovieSession.objects.all()
serializer_class = MovieSessionSerializer
filter_backends = [DjangoFilterBackend]
filterset_class = MovieSessionFilter

def get_serializer_class(self):
if self.action == "list":
return MovieSessionListSerializer

if self.action == "retrieve":
return MovieSessionDetailSerializer

return MovieSessionSerializer
12 changes: 12 additions & 0 deletions cinema_service/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"django.contrib.messages",
"django.contrib.staticfiles",
"rest_framework",
"django_filters",
"debug_toolbar",
"cinema",
"user",
Expand Down Expand Up @@ -136,3 +137,14 @@
# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"


REST_FRAMEWORK = {
"DEFAULT_FILTER_BACKENDS": (
"django_filters.rest_framework.DjangoFilterBackend",
),
}

REST_FRAMEWORK["DEFAULT_FILTER_BACKENDS"] = [
"django_filters.rest_framework.DjangoFilterBackend"
]
Binary file added db.sqlite3
Binary file not shown.

0 comments on commit f91b4d9

Please sign in to comment.