diff --git a/cinema/permissions.py b/cinema/permissions.py new file mode 100644 index 00000000..6f2f26cf --- /dev/null +++ b/cinema/permissions.py @@ -0,0 +1,8 @@ +from rest_framework.permissions import BasePermission, SAFE_METHODS + + +class IsAdminOrIfAuthenticatedReadOnly(BasePermission): + def has_permission(self, request, view): + if request.method in SAFE_METHODS and request.user.is_authenticated: + return True + return bool(request.user and request.user.is_staff) diff --git a/cinema/serializers.py b/cinema/serializers.py index 7a6ae20c..97171a3d 100644 --- a/cinema/serializers.py +++ b/cinema/serializers.py @@ -1,4 +1,5 @@ from django.db import transaction + from rest_framework import serializers from cinema.models import ( diff --git a/cinema/views.py b/cinema/views.py index a191bf5f..c41e959f 100644 --- a/cinema/views.py +++ b/cinema/views.py @@ -1,11 +1,19 @@ from datetime import datetime - from django.db.models import F, Count + from rest_framework import viewsets +from rest_framework.authentication import TokenAuthentication from rest_framework.pagination import PageNumberPagination +from rest_framework.mixins import ( + ListModelMixin, + CreateModelMixin, + RetrieveModelMixin +) +from rest_framework.permissions import AllowAny +from rest_framework.viewsets import GenericViewSet from cinema.models import Genre, Actor, CinemaHall, Movie, MovieSession, Order - +from cinema.permissions import IsAdminOrIfAuthenticatedReadOnly from cinema.serializers import ( GenreSerializer, ActorSerializer, @@ -21,32 +29,48 @@ ) -class GenreViewSet(viewsets.ModelViewSet): +class OrderPagination(PageNumberPagination): + page_size = 10 + max_page_size = 100 + + +class GenreViewSet(ListModelMixin, CreateModelMixin, GenericViewSet): queryset = Genre.objects.all() serializer_class = GenreSerializer + authentication_classes = (TokenAuthentication,) + permission_classes = (IsAdminOrIfAuthenticatedReadOnly, ) -class ActorViewSet(viewsets.ModelViewSet): +class ActorViewSet(ListModelMixin, CreateModelMixin, GenericViewSet): queryset = Actor.objects.all() serializer_class = ActorSerializer + authentication_classes = (TokenAuthentication,) + permission_classes = (IsAdminOrIfAuthenticatedReadOnly, ) -class CinemaHallViewSet(viewsets.ModelViewSet): +class CinemaHallViewSet(ListModelMixin, CreateModelMixin, GenericViewSet): queryset = CinemaHall.objects.all() serializer_class = CinemaHallSerializer + authentication_classes = (TokenAuthentication,) + permission_classes = (IsAdminOrIfAuthenticatedReadOnly, ) -class MovieViewSet(viewsets.ModelViewSet): +class MovieViewSet( + ListModelMixin, + CreateModelMixin, + RetrieveModelMixin, + GenericViewSet +): queryset = Movie.objects.prefetch_related("genres", "actors") serializer_class = MovieSerializer + authentication_classes = (TokenAuthentication,) + permission_classes = (IsAdminOrIfAuthenticatedReadOnly, ) @staticmethod def _params_to_ints(qs): - """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") @@ -69,10 +93,8 @@ def get_queryset(self): def get_serializer_class(self): if self.action == "list": return MovieListSerializer - if self.action == "retrieve": return MovieDetailSerializer - return MovieSerializer @@ -87,6 +109,8 @@ class MovieSessionViewSet(viewsets.ModelViewSet): ) ) serializer_class = MovieSessionSerializer + authentication_classes = (TokenAuthentication,) + permission_classes = (IsAdminOrIfAuthenticatedReadOnly, ) def get_queryset(self): date = self.request.query_params.get("date") @@ -106,24 +130,19 @@ def get_queryset(self): def get_serializer_class(self): if self.action == "list": return MovieSessionListSerializer - if self.action == "retrieve": return MovieSessionDetailSerializer - return MovieSessionSerializer -class OrderPagination(PageNumberPagination): - page_size = 10 - max_page_size = 100 - - -class OrderViewSet(viewsets.ModelViewSet): +class OrderViewSet(ListModelMixin, CreateModelMixin, GenericViewSet): queryset = Order.objects.prefetch_related( "tickets__movie_session__movie", "tickets__movie_session__cinema_hall" ) serializer_class = OrderSerializer pagination_class = OrderPagination + authentication_classes = (TokenAuthentication,) + permission_classes = (IsAdminOrIfAuthenticatedReadOnly, ) def get_queryset(self): return Order.objects.filter(user=self.request.user) @@ -131,8 +150,9 @@ def get_queryset(self): def get_serializer_class(self): if self.action == "list": return OrderListSerializer - return OrderSerializer - def perform_create(self, serializer): - serializer.save(user=self.request.user) + def get_permissions(self): + if self.action == "create": + return [AllowAny()] + return [IsAdminOrIfAuthenticatedReadOnly()] diff --git a/cinema_service/settings.py b/cinema_service/settings.py index 29ea7dea..062c0ac0 100644 --- a/cinema_service/settings.py +++ b/cinema_service/settings.py @@ -125,7 +125,7 @@ USE_I18N = True -USE_TZ = False +USE_TZ = True # Static files (CSS, JavaScript, Images) diff --git a/cinema_service/urls.py b/cinema_service/urls.py index bf903c00..a282f874 100644 --- a/cinema_service/urls.py +++ b/cinema_service/urls.py @@ -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")), ] diff --git a/user/serializers.py b/user/serializers.py index fa56336e..43d3d34c 100644 --- a/user/serializers.py +++ b/user/serializers.py @@ -1 +1,27 @@ -# write your code here +from django.contrib.auth import get_user_model + +from rest_framework import serializers + + +class UserSerializer(serializers.ModelSerializer): + class Meta: + model = get_user_model() + fields = ("id", "username", "password", "email", "is_staff") + read_only_fields = ("id", "is_staff") + extra_kwargs = { + "password": { + "write_only": True, + "min_length": 5, + }, + } + + def create(self, validated_data): + return get_user_model().objects.create_user(**validated_data) + + def update(self, instance, validated_data): + password = validated_data.pop("password", ) + user = super().update(instance, validated_data) + if password: + user.set_password(password) + user.save() + return user diff --git a/user/urls.py b/user/urls.py index fa56336e..8bfbaf20 100644 --- a/user/urls.py +++ b/user/urls.py @@ -1 +1,12 @@ -# write your code here +from django.urls import path + +from user.views import CreateUserView, LoginUserView, ManageUserView + + +app_name = "user" + +urlpatterns = [ + path("register/", CreateUserView.as_view(), name="create"), + path("login/", LoginUserView.as_view(), name="login"), + path("me/", ManageUserView.as_view(), name="manage"), +] diff --git a/user/views.py b/user/views.py index fa56336e..9496bc6c 100644 --- a/user/views.py +++ b/user/views.py @@ -1 +1,24 @@ -# write your code here +from rest_framework import generics +from rest_framework.authentication import TokenAuthentication +from rest_framework.authtoken.views import ObtainAuthToken +from rest_framework.permissions import IsAuthenticated +from rest_framework.settings import api_settings + +from user.serializers import UserSerializer + + +class CreateUserView(generics.CreateAPIView): + serializer_class = UserSerializer + + +class LoginUserView(ObtainAuthToken): + renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES + + +class ManageUserView(generics.RetrieveUpdateAPIView): + serializer_class = UserSerializer + authentication_classes = (TokenAuthentication, ) + permission_classes = (IsAuthenticated, ) + + def get_object(self): + return self.request.user