diff --git a/cinema/permissions.py b/cinema/permissions.py new file mode 100644 index 00000000..d9c28647 --- /dev/null +++ b/cinema/permissions.py @@ -0,0 +1,10 @@ +from rest_framework.permissions import BasePermission, SAFE_METHODS + + +class IsAdminOrIfAuthenticatedReadOnly(BasePermission): + def has_permission(self, request, view): + return bool( + request.method in SAFE_METHODS + and request.user + and request.user.is_authenticated + ) or (request.user and request.user.is_staff) diff --git a/cinema/views.py b/cinema/views.py index a191bf5f..253fa877 100644 --- a/cinema/views.py +++ b/cinema/views.py @@ -1,42 +1,62 @@ from datetime import datetime -from django.db.models import F, Count -from rest_framework import viewsets +from django.db.models import Count, F +from rest_framework.mixins import ( + CreateModelMixin, + DestroyModelMixin, + ListModelMixin, + RetrieveModelMixin, + UpdateModelMixin, +) from rest_framework.pagination import PageNumberPagination +from rest_framework.permissions import IsAuthenticated +from rest_framework.viewsets import GenericViewSet -from cinema.models import Genre, Actor, CinemaHall, Movie, MovieSession, Order - +from cinema.models import Actor, CinemaHall, Genre, Movie, MovieSession, Order from cinema.serializers import ( - GenreSerializer, ActorSerializer, CinemaHallSerializer, - MovieSerializer, - MovieSessionSerializer, - MovieSessionListSerializer, + GenreSerializer, MovieDetailSerializer, - MovieSessionDetailSerializer, MovieListSerializer, - OrderSerializer, + MovieSerializer, + MovieSessionDetailSerializer, + MovieSessionListSerializer, + MovieSessionSerializer, OrderListSerializer, + OrderSerializer, ) -class GenreViewSet(viewsets.ModelViewSet): +class GenreViewSet( + ListModelMixin, + CreateModelMixin, + GenericViewSet, +): queryset = Genre.objects.all() serializer_class = GenreSerializer -class ActorViewSet(viewsets.ModelViewSet): +class ActorViewSet( + CreateModelMixin, + ListModelMixin, + GenericViewSet, +): queryset = Actor.objects.all() serializer_class = ActorSerializer -class CinemaHallViewSet(viewsets.ModelViewSet): +class CinemaHallViewSet(ListModelMixin, CreateModelMixin, GenericViewSet): queryset = CinemaHall.objects.all() serializer_class = CinemaHallSerializer -class MovieViewSet(viewsets.ModelViewSet): +class MovieViewSet( + ListModelMixin, + CreateModelMixin, + RetrieveModelMixin, + GenericViewSet, +): queryset = Movie.objects.prefetch_related("genres", "actors") serializer_class = MovieSerializer @@ -76,7 +96,14 @@ def get_serializer_class(self): return MovieSerializer -class MovieSessionViewSet(viewsets.ModelViewSet): +class MovieSessionViewSet( + ListModelMixin, + CreateModelMixin, + RetrieveModelMixin, + UpdateModelMixin, + DestroyModelMixin, + GenericViewSet, +): queryset = ( MovieSession.objects.all() .select_related("movie", "cinema_hall") @@ -118,13 +145,18 @@ class OrderPagination(PageNumberPagination): 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 + def get_permissions(self): + if self.action == "create": + return (IsAuthenticated(),) + return super().get_permissions() + def get_queryset(self): return Order.objects.filter(user=self.request.user) diff --git a/cinema_service/settings.py b/cinema_service/settings.py index 29ea7dea..3b31f8f6 100644 --- a/cinema_service/settings.py +++ b/cinema_service/settings.py @@ -15,7 +15,6 @@ # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent - # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/ @@ -80,7 +79,6 @@ WSGI_APPLICATION = "cinema_service.wsgi.application" - # Database # https://docs.djangoproject.com/en/4.0/ref/settings/#databases @@ -91,7 +89,6 @@ } } - # Password validation # https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators @@ -121,12 +118,11 @@ LANGUAGE_CODE = "en-us" -TIME_ZONE = "UTC" +TIME_ZONE = "Europe/Kiev" USE_I18N = True -USE_TZ = False - +USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/4.0/howto/static-files/ @@ -137,3 +133,12 @@ # https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" + +REST_FRAMEWORK = { + "DEFAULT_AUTHENTICATION_CLASSES": [ + "rest_framework.authentication.TokenAuthentication", + ], + "DEFAULT_PERMISSION_CLASSES": [ + "cinema.permissions.IsAdminOrIfAuthenticatedReadOnly", + ], +} diff --git a/cinema_service/urls.py b/cinema_service/urls.py index bf903c00..279ce746 100644 --- a/cinema_service/urls.py +++ b/cinema_service/urls.py @@ -1,8 +1,9 @@ from django.contrib import admin -from django.urls import path, include +from django.urls import include, path 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..a44083d8 100644 --- a/user/serializers.py +++ b/user/serializers.py @@ -1 +1,33 @@ -# 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", + "email", + "password", + "is_staff", + ) + read_only_fields = ("id", "is_staff") + extra_kwargs = { + "password": { + "write_only": True, + "min_length": 4, + } + } + + 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", None) + 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..82fb2c9c 100644 --- a/user/urls.py +++ b/user/urls.py @@ -1 +1,11 @@ -# 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..1780db07 100644 --- a/user/views.py +++ b/user/views.py @@ -1 +1,23 @@ -# write your code here +from rest_framework import generics +from rest_framework.authtoken.views import ObtainAuthToken +from rest_framework.permissions import AllowAny, IsAuthenticated +from rest_framework.settings import api_settings + +from user.serializers import UserSerializer + + +class CreateUserView(generics.CreateAPIView): + serializer_class = UserSerializer + permission_classes = (AllowAny,) + + +class LoginUserView(ObtainAuthToken): + renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES + + +class ManageUserView(generics.RetrieveUpdateAPIView): + serializer_class = UserSerializer + permission_classes = (IsAuthenticated,) + + def get_object(self): + return self.request.user