Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Solution #709

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions cinema/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,8 @@ def __str__(self):

class Order(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE
)
user = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)

def __str__(self):
return str(self.created_at)
Expand All @@ -76,11 +75,11 @@ class Meta:

class Ticket(models.Model):
movie_session = models.ForeignKey(
MovieSession, on_delete=models.CASCADE, related_name="tickets"
)
order = models.ForeignKey(
Order, on_delete=models.CASCADE, related_name="tickets"
MovieSession, on_delete=models.CASCADE,
related_name="tickets"
)
order = models.ForeignKey(Order, on_delete=models.CASCADE,
related_name="tickets")
row = models.IntegerField()
seat = models.IntegerField()

Expand Down Expand Up @@ -122,8 +121,10 @@ def save(
)

def __str__(self):
return (f"{str(self.movie_session)} "
f"(row: {self.row}, seat: {self.seat})")
return (
f"{str(self.movie_session)} "
f"(row: {self.row}, seat: {self.seat})"
)

class Meta:
unique_together = ("movie_session", "row", "seat")
Expand Down
8 changes: 8 additions & 0 deletions cinema/permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from rest_framework import permissions


class IsAdminOrIfAuthenticatedReadOnly(permissions.BasePermission):
def has_permission(self, request, view):
if request.method in permissions.SAFE_METHODS:
return True
return request.user and request.user.is_staff
23 changes: 11 additions & 12 deletions cinema/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ class Meta:


class MovieListSerializer(MovieSerializer):
genres = serializers.SlugRelatedField(
many=True, read_only=True, slug_field="name"
)
genres = serializers.SlugRelatedField(many=True,
read_only=True,
slug_field="name")
actors = serializers.SlugRelatedField(
many=True, read_only=True, slug_field="full_name"
)
Expand All @@ -62,9 +62,8 @@ class Meta:

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_name = serializers.CharField(source="cinema_hall.name",
read_only=True)
cinema_hall_capacity = serializers.IntegerField(
source="cinema_hall.capacity", read_only=True
)
Expand All @@ -85,9 +84,9 @@ class Meta:
class TicketSerializer(serializers.ModelSerializer):
def validate(self, attrs):
data = super(TicketSerializer, self).validate(attrs=attrs)
Ticket.validate_ticket(
attrs["row"], attrs["seat"], attrs["movie_session"]
)
Ticket.validate_ticket(attrs["row"],
attrs["seat"],
attrs["movie_session"])
return data

class Meta:
Expand All @@ -108,9 +107,9 @@ class Meta:
class MovieSessionDetailSerializer(MovieSessionSerializer):
movie = MovieListSerializer(many=False, read_only=True)
cinema_hall = CinemaHallSerializer(many=False, read_only=True)
taken_places = TicketSeatsSerializer(
source="tickets", many=True, read_only=True
)
taken_places = TicketSeatsSerializer(source="tickets",
many=True,
read_only=True)

class Meta:
model = MovieSession
Expand Down
12 changes: 3 additions & 9 deletions cinema/tests/test_movie_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,7 @@ def test_get_movies(self):
def test_retrieve_movie(self):
movie = sample_movie()
movie.genres.add(Genre.objects.create(name="Genre"))
movie.actors.add(
Actor.objects.create(first_name="Actor", last_name="Last")
)
movie.actors.add(Actor.objects.create(first_name="Actor", last_name="Last"))

url = detail_url(movie.id)
response = self.client.get(url)
Expand Down Expand Up @@ -114,16 +112,12 @@ def test_put_movie(self):
}

response = self.client.put(url, payload)
self.assertEqual(
response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED
)
self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED)

def test_delete_movie(self):
movie = sample_movie()

url = detail_url(movie.id)
response = self.client.delete(url)

self.assertEqual(
response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED
)
self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED)
74 changes: 60 additions & 14 deletions cinema/views.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
from datetime import datetime
import pytz

from django.db.models import F, Count
from rest_framework import viewsets
from rest_framework import viewsets, mixins
from rest_framework.pagination import PageNumberPagination
from rest_framework.permissions import IsAuthenticated, IsAdminUser

from cinema.models import Genre, Actor, CinemaHall, Movie, MovieSession, Order
from cinema.permissions import IsAdminOrIfAuthenticatedReadOnly

from cinema.serializers import (
GenreSerializer,
Expand All @@ -21,32 +24,70 @@
)


class GenreViewSet(viewsets.ModelViewSet):
class GenreViewSet(
mixins.ListModelMixin, mixins.CreateModelMixin, viewsets.GenericViewSet
):
queryset = Genre.objects.all()
serializer_class = GenreSerializer

def get_permissions(self):
if self.action == "create":
self.permission_classes = [IsAdminUser]
else:
self.permission_classes = [IsAuthenticated]
return super().get_permissions()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I understand task, you already have implemented IsAdminORIfAuthenticatedReadOnly. That's the same what you wrote in def get_permissions. So like python dev - probably should just wrote permission_classes = (IsAdminOrIfAuthenticatedReadOnly,) and no need to write for every ViewSet +7 code lines, just add one attribute.


class ActorViewSet(viewsets.ModelViewSet):

class ActorViewSet(
mixins.ListModelMixin, mixins.CreateModelMixin, viewsets.GenericViewSet
):
queryset = Actor.objects.all()
serializer_class = ActorSerializer

def get_permissions(self):
if self.action == "create":
self.permission_classes = [IsAdminUser]
else:
self.permission_classes = [IsAuthenticated]
return super().get_permissions()


class CinemaHallViewSet(viewsets.ModelViewSet):
class CinemaHallViewSet(
mixins.ListModelMixin, mixins.CreateModelMixin, viewsets.GenericViewSet
):
queryset = CinemaHall.objects.all()
serializer_class = CinemaHallSerializer

def get_permissions(self):
if self.action == "create":
self.permission_classes = [IsAdminUser]
else:
self.permission_classes = [IsAuthenticated]
return super().get_permissions()


class MovieViewSet(viewsets.ModelViewSet):
class MovieViewSet(
mixins.ListModelMixin,
mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
viewsets.GenericViewSet,
):
queryset = Movie.objects.prefetch_related("genres", "actors")
serializer_class = MovieSerializer

def get_permissions(self):
if self.action == "create":
self.permission_classes = [IsAdminUser]
else:
self.permission_classes = [IsAuthenticated]
return super().get_permissions()

@staticmethod
def _params_to_ints(qs):
"""Converts a list of string IDs to a list of integers"""
"""Converts a list of string IDs to a list of integers."""
return [int(str_id) for str_id in qs.split(",")]

def get_queryset(self):
"""Retrieve the movies with filters"""
title = self.request.query_params.get("title")
genres = self.request.query_params.get("genres")
actors = self.request.query_params.get("actors")
Expand All @@ -69,10 +110,8 @@ def get_queryset(self):
def get_serializer_class(self):
if self.action == "list":
return MovieListSerializer

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

return MovieSerializer


Expand All @@ -88,14 +127,21 @@ class MovieSessionViewSet(viewsets.ModelViewSet):
)
serializer_class = MovieSessionSerializer

def get_permissions(self):
if self.action in ["create", "update", "partial_update", "destroy"]:
self.permission_classes = [IsAdminUser]
else:
self.permission_classes = [IsAuthenticated]
return super().get_permissions()

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

queryset = self.queryset

if date:
date = datetime.strptime(date, "%Y-%m-%d").date()
date = datetime.strptime(date, "%Y-%m-%d").replace(tzinfo=pytz.UTC)
queryset = queryset.filter(show_time__date=date)

if movie_id_str:
Expand All @@ -106,10 +152,8 @@ def get_queryset(self):
def get_serializer_class(self):
if self.action == "list":
return MovieSessionListSerializer

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

return MovieSessionSerializer


Expand All @@ -118,20 +162,22 @@ class OrderPagination(PageNumberPagination):
max_page_size = 100


class OrderViewSet(viewsets.ModelViewSet):
class OrderViewSet(
mixins.ListModelMixin, mixins.CreateModelMixin, viewsets.GenericViewSet
):
queryset = Order.objects.prefetch_related(
"tickets__movie_session__movie", "tickets__movie_session__cinema_hall"
)
serializer_class = OrderSerializer
pagination_class = OrderPagination
permission_classes = [IsAuthenticated]

def get_queryset(self):
return Order.objects.filter(user=self.request.user)

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

return OrderSerializer

def perform_create(self, serializer):
Expand Down
21 changes: 15 additions & 6 deletions cinema_service/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,8 @@
# See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = (
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
Expand Down Expand Up @@ -102,15 +101,15 @@
},
{
"NAME": "django.contrib.auth.password_validation."
"MinimumLengthValidator",
"MinimumLengthValidator",
},
{
"NAME": "django.contrib.auth.password_validation."
"CommonPasswordValidator",
"CommonPasswordValidator",
},
{
"NAME": "django.contrib.auth.password_validation."
"NumericPasswordValidator",
"NumericPasswordValidator",
},
]

Expand All @@ -125,7 +124,7 @@

USE_I18N = True

USE_TZ = False
USE_TZ = True


# Static files (CSS, JavaScript, Images)
Expand All @@ -137,3 +136,13 @@
# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

REST_FRAMEWORK = {
"DEFAULT_PERMISSION_CLASSES": [
"rest_framework.permissions.IsAuthenticated",
],
"DEFAULT_AUTHENTICATION_CLASSES": [
"rest_framework.authentication.TokenAuthentication",
"rest_framework.authentication.SessionAuthentication",
],
}
1 change: 1 addition & 0 deletions cinema_service/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
urlpatterns = [
path("admin/", admin.site.urls),
path("api/cinema/", include("cinema.urls", namespace="cinema")),
path("api/user/", include("user.urls", namespace="user")),
path("__debug__/", include("debug_toolbar.urls")),
]
Loading
Loading