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 #667

Open
wants to merge 4 commits 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
16 changes: 16 additions & 0 deletions cinema/permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from rest_framework.permissions import BasePermission, SAFE_METHODS

Choose a reason for hiding this comment

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

The import statement is correct. SAFE_METHODS is used to check if the request method is read-only, which is appropriate for this permission class.



class IsAdminOrIfAuthenticatedReadOnly(BasePermission):
def has_permission(self, request, view):
if request.user and request.user.is_staff:
return True

if request.method in SAFE_METHODS:
return request.user and request.user.is_authenticated

if (view.__class__.__name__ == "OrderViewSet"
and request.method == "POST"):
return request.user and request.user.is_authenticated
Comment on lines +12 to +14

Choose a reason for hiding this comment

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

The logic here correctly checks if the view is OrderViewSet and the method is POST, allowing authenticated users to create orders. This aligns with the task requirement to allow authenticated users to perform the create action on OrderViewSet.


return False
23 changes: 22 additions & 1 deletion cinema/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,18 @@

from django.db.models import F, Count
from rest_framework import viewsets
from rest_framework.authentication import TokenAuthentication

Choose a reason for hiding this comment

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

The use of TokenAuthentication is appropriate for securing the API endpoints, ensuring that only authenticated users can access them.

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,
Order
)
from cinema.permissions import IsAdminOrIfAuthenticatedReadOnly

Choose a reason for hiding this comment

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

The import of IsAdminOrIfAuthenticatedReadOnly from cinema.permissions is correct, ensuring that the permission logic is applied to the viewsets.


from cinema.serializers import (
GenreSerializer,
Expand All @@ -24,21 +33,29 @@
class GenreViewSet(viewsets.ModelViewSet):
queryset = Genre.objects.all()
serializer_class = GenreSerializer
authentication_classes = (TokenAuthentication,)
permission_classes = (IsAdminOrIfAuthenticatedReadOnly,)

Choose a reason for hiding this comment

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

The GenreViewSet correctly applies the IsAdminOrIfAuthenticatedReadOnly permission class, ensuring that only authenticated users can perform safe methods and only admin users can perform other actions.



class ActorViewSet(viewsets.ModelViewSet):
queryset = Actor.objects.all()
serializer_class = ActorSerializer
authentication_classes = (TokenAuthentication,)
permission_classes = (IsAdminOrIfAuthenticatedReadOnly,)

Choose a reason for hiding this comment

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

The ActorViewSet is correctly configured with the necessary authentication and permission classes, similar to the GenreViewSet.



class CinemaHallViewSet(viewsets.ModelViewSet):
queryset = CinemaHall.objects.all()
serializer_class = CinemaHallSerializer
authentication_classes = (TokenAuthentication,)
permission_classes = (IsAdminOrIfAuthenticatedReadOnly,)

Choose a reason for hiding this comment

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

The CinemaHallViewSet also correctly applies the authentication and permission classes, maintaining consistency across the viewsets.



class MovieViewSet(viewsets.ModelViewSet):
queryset = Movie.objects.prefetch_related("genres", "actors")
serializer_class = MovieSerializer
authentication_classes = (TokenAuthentication,)
permission_classes = (IsAdminOrIfAuthenticatedReadOnly,)

Choose a reason for hiding this comment

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

The MovieViewSet is correctly configured with authentication and permission classes, and the filtering logic in get_queryset is implemented correctly.


@staticmethod
def _params_to_ints(qs):
Expand Down Expand Up @@ -87,6 +104,8 @@ class MovieSessionViewSet(viewsets.ModelViewSet):
)
)
serializer_class = MovieSessionSerializer
authentication_classes = (TokenAuthentication,)
permission_classes = (IsAdminOrIfAuthenticatedReadOnly,)

Choose a reason for hiding this comment

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

The MovieSessionViewSet is correctly configured with authentication and permission classes, and the filtering logic in get_queryset is implemented correctly.


def get_queryset(self):
date = self.request.query_params.get("date")
Expand Down Expand Up @@ -124,6 +143,8 @@ class OrderViewSet(viewsets.ModelViewSet):
)
serializer_class = OrderSerializer
pagination_class = OrderPagination
authentication_classes = (TokenAuthentication,)
permission_classes = (IsAdminOrIfAuthenticatedReadOnly,)

Choose a reason for hiding this comment

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

The OrderViewSet is correctly configured with authentication and permission classes, and the get_queryset method correctly filters orders by the current user.


def get_queryset(self):
return Order.objects.filter(user=self.request.user)
Expand Down
32 changes: 18 additions & 14 deletions cinema_service/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,22 @@
"""

from pathlib import Path
import os
from dotenv import load_dotenv


# 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/
load_dotenv()

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = (
"django-insecure-6vubhk2$++agnctay_4pxy_8cq)mosmn(*-#2b^v4cgsh-^!i3"
)
SECRET_KEY = os.getenv("SECRET_KEY")

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
DEBUG = os.getenv("DEBUG") == "True"

ALLOWED_HOSTS = []

Expand Down Expand Up @@ -80,7 +81,6 @@

WSGI_APPLICATION = "cinema_service.wsgi.application"


# Database
# https://docs.djangoproject.com/en/4.0/ref/settings/#databases

Expand All @@ -91,26 +91,25 @@
}
}


# Password validation
# https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
{
"NAME": "django.contrib.auth.password_validation."
"UserAttributeSimilarityValidator",
"UserAttributeSimilarityValidator",
},
{
"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 @@ -121,12 +120,11 @@

LANGUAGE_CODE = "en-us"

TIME_ZONE = "UTC"
TIME_ZONE = "Europe/Kiev"

Choose a reason for hiding this comment

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

The TIME_ZONE is set to 'Europe/Kiev'. Ensure this is the desired time zone for your application, or adjust it according to your needs.


USE_I18N = True

USE_TZ = False

USE_TZ = True

Choose a reason for hiding this comment

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

The USE_TZ setting is enabled, which is generally recommended for handling time zones correctly in Django.


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.0/howto/static-files/
Expand All @@ -137,3 +135,9 @@
# 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",

Choose a reason for hiding this comment

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

The DEFAULT_PERMISSION_CLASSES is set to IsAuthenticated, which means all API endpoints will require authentication by default. Ensure this aligns with your application's requirements.

]
}
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")),

Choose a reason for hiding this comment

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

The inclusion of user.urls with a namespace is correct, ensuring that URL names are unique across the project.

Choose a reason for hiding this comment

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

Ensure that the debug_toolbar is only included in development environments. In production, this should be removed or conditionally included based on the DEBUG setting.

path("__debug__/", include("debug_toolbar.urls")),
]
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ flake8-quotes==3.3.1
flake8-variables-names==0.0.5
pep8-naming==0.13.2
django-debug-toolbar==3.2.4
djangorestframework==3.13.1
djangorestframework==3.13.1
python-dotenv~=1.0.1
28 changes: 27 additions & 1 deletion user/serializers.py
Original file line number Diff line number Diff line change
@@ -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", "email", "password", "is_staff")
read_only_fields = ("id", "is_staff")

Choose a reason for hiding this comment

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

The fields attribute correctly includes necessary user fields, and the read_only_fields ensures that id and is_staff cannot be modified through the serializer.

extra_kwargs = {
"password": {"write_only": True,
"min_length": 5}
}

Choose a reason for hiding this comment

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

The extra_kwargs for the password field correctly enforce it as write-only and set a minimum length, enhancing security.


def create(self, validated_data):
return get_user_model().objects.create_user(**validated_data)

Choose a reason for hiding this comment

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

The create method correctly uses create_user to ensure the password is hashed when a new user is created.

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
13 changes: 12 additions & 1 deletion user/urls.py
Original file line number Diff line number Diff line change
@@ -1 +1,12 @@
# write your code here
from django.urls import path


from user.views import CreateUserView, CreateTokenView, ManageUserView

app_name = "user"

Choose a reason for hiding this comment

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

The app_name is correctly set to 'user', which is useful for namespacing the URLs in this app.

urlpatterns = [
path("register/", CreateUserView.as_view(), name="create"),
path("login/", CreateTokenView.as_view(), name="login"),
path("me/", ManageUserView.as_view(), name="manage"),
]

Choose a reason for hiding this comment

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

The URL patterns are correctly defined for user registration, login, and management, linking to the appropriate views.

26 changes: 25 additions & 1 deletion user/views.py
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.authtoken.views import ObtainAuthToken
from rest_framework.settings import api_settings
from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import IsAuthenticated


from user.serializers import UserSerializer


class CreateUserView(generics.CreateAPIView):
serializer_class = UserSerializer

Choose a reason for hiding this comment

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

The CreateUserView correctly uses the UserSerializer to handle user creation.


class CreateTokenView(ObtainAuthToken):
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES

Choose a reason for hiding this comment

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

The CreateTokenView uses the default renderer classes, which is appropriate for handling token authentication.


class ManageUserView(generics.RetrieveUpdateAPIView):
serializer_class = UserSerializer
authentication_classes = (TokenAuthentication,)
permission_classes = (IsAuthenticated,)

Choose a reason for hiding this comment

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

The ManageUserView is correctly configured with TokenAuthentication and IsAuthenticated permission, ensuring that only authenticated users can access and update their information.

def get_object(self):
return self.request.user
Loading