-
Notifications
You must be signed in to change notification settings - Fork 693
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 #698
base: master
Are you sure you want to change the base?
solution #698
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,12 @@ | ||
from datetime import datetime | ||
|
||
from django.db.models import F, Count | ||
from rest_framework import viewsets | ||
from rest_framework import mixins | ||
from rest_framework.authentication import TokenAuthentication | ||
from rest_framework.pagination import PageNumberPagination | ||
from rest_framework.permissions import IsAuthenticated | ||
from rest_framework.viewsets import GenericViewSet, ModelViewSet | ||
from rest_framework.exceptions import PermissionDenied | ||
|
||
from cinema.models import Genre, Actor, CinemaHall, Movie, MovieSession, Order | ||
|
||
|
@@ -17,66 +21,84 @@ | |
MovieSessionDetailSerializer, | ||
MovieListSerializer, | ||
OrderSerializer, | ||
OrderListSerializer, | ||
) | ||
|
||
|
||
class GenreViewSet(viewsets.ModelViewSet): | ||
from user.permissions import IsAdminOrIfAuthenticatedReadOnly | ||
|
||
|
||
class GenreViewSet( | ||
mixins.ListModelMixin, | ||
mixins.CreateModelMixin, | ||
GenericViewSet, | ||
): | ||
queryset = Genre.objects.all() | ||
serializer_class = GenreSerializer | ||
authentication_classes = (TokenAuthentication,) | ||
permission_classes = [IsAdminOrIfAuthenticatedReadOnly] | ||
|
||
|
||
class ActorViewSet(viewsets.ModelViewSet): | ||
class ActorViewSet( | ||
mixins.ListModelMixin, | ||
mixins.CreateModelMixin, | ||
GenericViewSet, | ||
): | ||
queryset = Actor.objects.all() | ||
serializer_class = ActorSerializer | ||
authentication_classes = (TokenAuthentication,) | ||
permission_classes = [IsAdminOrIfAuthenticatedReadOnly] | ||
|
||
|
||
class CinemaHallViewSet(viewsets.ModelViewSet): | ||
class CinemaHallViewSet( | ||
mixins.ListModelMixin, | ||
mixins.CreateModelMixin, | ||
GenericViewSet, | ||
): | ||
queryset = CinemaHall.objects.all() | ||
serializer_class = CinemaHallSerializer | ||
authentication_classes = (TokenAuthentication,) | ||
permission_classes = [IsAdminOrIfAuthenticatedReadOnly] | ||
|
||
|
||
class MovieViewSet(viewsets.ModelViewSet): | ||
class MovieViewSet( | ||
mixins.ListModelMixin, | ||
mixins.CreateModelMixin, | ||
mixins.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") | ||
|
||
queryset = self.queryset | ||
|
||
if title: | ||
queryset = queryset.filter(title__icontains=title) | ||
|
||
if genres: | ||
genres_ids = self._params_to_ints(genres) | ||
queryset = queryset.filter(genres__id__in=genres_ids) | ||
|
||
if actors: | ||
actors_ids = self._params_to_ints(actors) | ||
queryset = queryset.filter(actors__id__in=actors_ids) | ||
|
||
return queryset.distinct() | ||
|
||
def get_serializer_class(self): | ||
if self.action == "list": | ||
return MovieListSerializer | ||
|
||
if self.action == "retrieve": | ||
return MovieDetailSerializer | ||
|
||
return MovieSerializer | ||
|
||
|
||
class MovieSessionViewSet(viewsets.ModelViewSet): | ||
class MovieSessionViewSet(ModelViewSet): | ||
queryset = ( | ||
MovieSession.objects.all() | ||
.select_related("movie", "cinema_hall") | ||
|
@@ -87,6 +109,8 @@ class MovieSessionViewSet(viewsets.ModelViewSet): | |
) | ||
) | ||
serializer_class = MovieSessionSerializer | ||
permission_classes = [IsAdminOrIfAuthenticatedReadOnly] | ||
authentication_classes = (TokenAuthentication,) | ||
|
||
def get_queryset(self): | ||
date = self.request.query_params.get("date") | ||
|
@@ -112,27 +136,52 @@ def get_serializer_class(self): | |
|
||
return MovieSessionSerializer | ||
|
||
def perform_create(self, serializer): | ||
if not self.request.user.is_staff: | ||
raise PermissionDenied( | ||
"You do not have permission to perform this action." | ||
) | ||
serializer.save() | ||
|
||
def perform_update(self, serializer): | ||
if not self.request.user.is_staff: | ||
raise PermissionDenied( | ||
"You do not have permission to perform this action." | ||
) | ||
Comment on lines
+146
to
+150
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Raising a |
||
serializer.save() | ||
|
||
def perform_destroy(self, instance): | ||
if not self.request.user.is_staff: | ||
raise PermissionDenied( | ||
"You do not have permission to perform this action." | ||
) | ||
Comment on lines
+153
to
+157
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Raising a |
||
instance.delete() | ||
|
||
|
||
class OrderPagination(PageNumberPagination): | ||
page_size = 10 | ||
max_page_size = 100 | ||
|
||
|
||
class OrderViewSet(viewsets.ModelViewSet): | ||
class OrderViewSet( | ||
mixins.ListModelMixin, | ||
mixins.CreateModelMixin, | ||
GenericViewSet, | ||
): | ||
queryset = Order.objects.prefetch_related( | ||
"tickets__movie_session__movie", "tickets__movie_session__cinema_hall" | ||
"tickets__movie_session__movie", | ||
"tickets__movie_session__cinema_hall" | ||
) | ||
serializer_class = OrderSerializer | ||
pagination_class = OrderPagination | ||
|
||
def get_queryset(self): | ||
return Order.objects.filter(user=self.request.user) | ||
authentication_classes = (TokenAuthentication,) | ||
permission_classes = [IsAdminOrIfAuthenticatedReadOnly] | ||
|
||
def get_serializer_class(self): | ||
if self.action == "list": | ||
return OrderListSerializer | ||
def get_permissions(self): | ||
if self.action == "create": | ||
return [IsAuthenticated()] | ||
Comment on lines
+181
to
+183
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
return super().get_permissions() | ||
|
||
return OrderSerializer | ||
|
||
def perform_create(self, serializer): | ||
serializer.save(user=self.request.user) | ||
def get_queryset(self): | ||
return Order.objects.filter(user=self.request.user) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,11 @@ | ||
from django.contrib import admin | ||
from django.urls import path, include | ||
from rest_framework.authtoken import views | ||
|
||
urlpatterns = [ | ||
path("admin/", admin.site.urls), | ||
path("api/token-auth/", views.obtain_auth_token, name="token-auth"), | ||
path("api/cinema/", include("cinema.urls", namespace="cinema")), | ||
sofiiasavkova marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ensure that the 'cinema/urls.py' file defines an |
||
path("api/user/", include("user.urls")), | ||
path("__debug__/", include("debug_toolbar.urls")), | ||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
from rest_framework.permissions import BasePermission, SAFE_METHODS | ||
from rest_framework.exceptions import NotAuthenticated | ||
|
||
|
||
class IsAdminOrIfAuthenticatedReadOnly(BasePermission): | ||
def has_permission(self, request, view): | ||
if not request.user.is_authenticated: | ||
raise NotAuthenticated() | ||
sofiiasavkova marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Raising a |
||
|
||
if request.method in SAFE_METHODS and request.user.is_authenticated: | ||
return True | ||
|
||
return request.user.is_staff |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,61 @@ | ||
# write your code here | ||
from django.contrib.auth import get_user_model | ||
from django.contrib.auth.password_validation import validate_password | ||
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": 5} | ||
} | ||
|
||
def create(self, validated_data): | ||
user = get_user_model().objects.create_user(**validated_data) | ||
return user | ||
sofiiasavkova marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
def update(self, instance, validated_data): | ||
password = validated_data.pop("password", None) | ||
user = super().update(instance, validated_data) | ||
|
||
if password: | ||
validate_password(password, user) # Validate the new password | ||
user.set_password(password) | ||
sofiiasavkova marked this conversation as resolved.
Show resolved
Hide resolved
|
||
user.save() | ||
|
||
return user | ||
|
||
|
||
class AuthTokenSerializer(serializers.Serializer): | ||
username = serializers.CharField() | ||
password = serializers.CharField( | ||
style={"input_type": "password"}, trim_whitespace=False | ||
) | ||
|
||
def validate(self, attrs): | ||
from django.contrib.auth import authenticate | ||
|
||
username = attrs.get("username") | ||
password = attrs.get("password") | ||
|
||
if username and password: | ||
user = authenticate( | ||
request=self.context.get("request"), | ||
username=username, | ||
password=password | ||
) | ||
if not user: | ||
raise serializers.ValidationError( | ||
"Unable to log in with provided credentials.", | ||
code="authorization", | ||
) | ||
else: | ||
sofiiasavkova marked this conversation as resolved.
Show resolved
Hide resolved
|
||
raise serializers.ValidationError( | ||
"Must include 'username' and 'password'.", | ||
code="authorization", | ||
) | ||
|
||
attrs["user"] = user | ||
return attrs |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,14 @@ | ||
# write your code here | ||
from django.urls import path | ||
from user.views import ( | ||
CreateUserView, | ||
CreateTokenView, | ||
ManageUserView | ||
) | ||
|
||
app_name = "user" | ||
|
||
urlpatterns = [ | ||
path("register/", CreateUserView.as_view(), name="create"), | ||
path("login/", CreateTokenView.as_view(), name="login"), | ||
path("me/", ManageUserView.as_view(), name="manage"), | ||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,25 @@ | ||
# 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, AuthTokenSerializer | ||
|
||
|
||
class CreateUserView(generics.CreateAPIView): | ||
serializer_class = UserSerializer | ||
|
||
|
||
class CreateTokenView(ObtainAuthToken): | ||
serializer_class = AuthTokenSerializer | ||
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Raising a
PermissionDenied
exception directly in theperform_create
method might lead to inconsistent behavior. Consider using permission classes to handle these checks more uniformly.