Skip to content

Commit

Permalink
Solution
Browse files Browse the repository at this point in the history
  • Loading branch information
Den-k0 committed Dec 13, 2024
1 parent efd4b75 commit 5c41f2f
Show file tree
Hide file tree
Showing 6 changed files with 195 additions and 7 deletions.
2 changes: 1 addition & 1 deletion .flake8
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[flake8]
inline-quotes = "
ignore = E203, E266, W503, N807, N818, F401
ignore = E203, E266, W503, N807, N818, F401, E126
max-line-length = 79
max-complexity = 18
select = B,C,E,F,W,T4,B9,Q0,N8,VNE
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ venv/
.pytest_cache/
**__pycache__/
*.pyc
app/db.sqlite3
app/db.sqlite3
db.sqlite3
83 changes: 81 additions & 2 deletions cinema/serializers.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
from django.db import transaction
from rest_framework import serializers
from rest_framework.validators import UniqueTogetherValidator

from cinema.models import Genre, Actor, CinemaHall, Movie, MovieSession
from cinema.models import (
Genre,
Actor,
CinemaHall,
Movie,
MovieSession,
Ticket,
Order
)


class GenreSerializer(serializers.ModelSerializer):
Expand Down Expand Up @@ -59,6 +69,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
Expand All @@ -68,13 +79,81 @@ class Meta:
"movie_title",
"cinema_hall_name",
"cinema_hall_capacity",
"tickets_available",
)


class TicketSerializer(serializers.ModelSerializer):
movie_session = MovieSessionSerializer()

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

def validate(self, attrs):
movie_session = attrs.get("movie_session")
row = attrs.get("row")
seat = attrs.get("seat")
cinema_hall = movie_session.cinema_hall

for ticket_attr_value, ticket_attr_name, cinema_hall_attr_name in [
(row, "row", "rows"),
(seat, "seat", "seats_in_row"),
]:
count_attrs = getattr(cinema_hall, cinema_hall_attr_name)
if not (1 <= ticket_attr_value <= count_attrs):
raise serializers.ValidationError(
{
ticket_attr_name: f"{ticket_attr_name} "
f"number must be in available range: "
f"(1, {cinema_hall_attr_name}): "
f"(1, {count_attrs})"
}
)
return attrs


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):
return [
{"row": ticket.row, "seat": ticket.seat}
for ticket in obj.tickets.all()
]


class OrderSerializer(serializers.ModelSerializer):
tickets = TicketSerializer(many=True, read_only=False, allow_empty=False)

class Meta:
model = Order
fields = ("id", "created_at", "tickets")
validators = [
UniqueTogetherValidator(
queryset=Ticket.objects.all(),
fields=["movie_session", "row", "seat"]
)
]

def create(self, validated_data):
with transaction.atomic():
tickets_date = validated_data.pop("tickets")
order = Order.objects.create(**validated_data)
for ticket_data in tickets_date:
Ticket.objects.create(order=order, **ticket_data)
return order


class TicketListSerializer(TicketSerializer):
movie_session = MovieSessionListSerializer(read_only=True)


class OrderListSerializer(OrderSerializer):
tickets = TicketListSerializer(read_only=True, many=True)
2 changes: 2 additions & 0 deletions cinema/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
CinemaHallViewSet,
MovieViewSet,
MovieSessionViewSet,
OrderViewSet,
)

router = routers.DefaultRouter()
Expand All @@ -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))]

Expand Down
104 changes: 102 additions & 2 deletions cinema/views.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
from django.db.models import Count, F
from django.utils.dateparse import parse_date
from rest_framework import viewsets
from rest_framework.pagination import PageNumberPagination

from cinema.models import (
Genre,
Actor,
CinemaHall,
Movie,
MovieSession,
Order
)

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

from cinema.serializers import (
GenreSerializer,
Expand All @@ -11,7 +22,7 @@
MovieSessionListSerializer,
MovieDetailSerializer,
MovieSessionDetailSerializer,
MovieListSerializer,
MovieListSerializer, OrderSerializer, OrderListSerializer,
)


Expand All @@ -34,6 +45,10 @@ class MovieViewSet(viewsets.ModelViewSet):
queryset = Movie.objects.all()
serializer_class = MovieSerializer

@staticmethod
def _params_to_ints(query_string):
return [int(str_id) for str_id in query_string.split(",")]

def get_serializer_class(self):
if self.action == "list":
return MovieListSerializer
Expand All @@ -43,6 +58,28 @@ def get_serializer_class(self):

return MovieSerializer

def get_queryset(self):
queryset = self.queryset

actors = self.request.query_params.get("actors")
if actors:
actors = self._params_to_ints(actors)
queryset = queryset.filter(actors__id__in=actors)

genres = self.request.query_params.get("genres")
if genres:
genres = self._params_to_ints(genres)
queryset = queryset.filter(genres__id__in=genres)

title = self.request.query_params.get("title")
if title:
queryset = queryset.filter(title__icontains=title)

if self.action in ("list", "retrieve"):
return queryset.prefetch_related("actors", "genres")

return queryset.distinct()


class MovieSessionViewSet(viewsets.ModelViewSet):
queryset = MovieSession.objects.all()
Expand All @@ -56,3 +93,66 @@ def get_serializer_class(self):
return MovieSessionDetailSerializer

return MovieSessionSerializer

def get_queryset(self):
queryset = self.queryset
date = self.request.query_params.get("date")
movie = self.request.query_params.get("movie")

if date:
date = parse_date(date)
if date:
queryset = queryset.filter(show_time__date=date)

if movie:
queryset = queryset.filter(movie_id=movie)

if self.action in "list":
return (
queryset
.select_related()
.annotate(
tickets_available=(
F("cinema_hall__rows")
* F("cinema_hall__seats_in_row")
- Count("tickets")
)
)
)
elif self.action in "retrieve":
return queryset.select_related()
return queryset.distinct()


class OrderSetPagination(PageNumberPagination):
page_size = 2
page_size_query_param = "page_size"
max_page_size = 10


class OrderViewSet(viewsets.ModelViewSet):
queryset = Order.objects.all()
serializer_class = OrderSerializer
pagination_class = OrderSetPagination

def get_queryset(self):
queryset = self.queryset.filter(user=self.request.user)

if self.action == "list":
queryset = queryset.prefetch_related(
"tickets__movie_session__movie",
"tickets__movie_session__cinema_hall"
)

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
8 changes: 7 additions & 1 deletion cinema_service/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@

USE_I18N = True

USE_TZ = False
USE_TZ = True


# Static files (CSS, JavaScript, Images)
Expand All @@ -136,3 +136,9 @@
# 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": 2
# }

0 comments on commit 5c41f2f

Please sign in to comment.