-
Notifications
You must be signed in to change notification settings - Fork 687
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 #725
base: master
Are you sure you want to change the base?
Solution #725
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 |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# Generated by Django 4.1 on 2024-12-17 07:38 | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
("cinema", "0001_initial"), | ||
] | ||
|
||
operations = [ | ||
migrations.AddField( | ||
model_name="movie", | ||
name="image", | ||
field=models.ImageField(blank=True, null=True, upload_to="uploads/movies/"), | ||
), | ||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# Generated by Django 4.1 on 2024-12-17 06:02 | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
("cinema", "0002_movie_image"), | ||
] | ||
|
||
operations = [ | ||
migrations.AlterField( | ||
model_name="movie", | ||
name="duration", | ||
field=models.IntegerField(default=90), | ||
), | ||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,12 @@ | ||
import uuid | ||
from pathlib import Path | ||
|
||
from django.core.exceptions import ValidationError | ||
from django.db import models | ||
from django.conf import settings | ||
from django.utils.text import slugify | ||
from django.utils import timezone | ||
from datetime import datetime | ||
|
||
|
||
class CinemaHall(models.Model): | ||
|
@@ -38,16 +44,24 @@ def full_name(self): | |
class Movie(models.Model): | ||
title = models.CharField(max_length=255) | ||
description = models.TextField() | ||
duration = models.IntegerField() | ||
duration = models.IntegerField(default=90) | ||
genres = models.ManyToManyField(Genre) | ||
actors = models.ManyToManyField(Actor) | ||
image = models.ImageField(upload_to="uploads/movies/", blank=True) | ||
|
||
class Meta: | ||
ordering = ["title"] | ||
|
||
def __str__(self): | ||
return self.title | ||
|
||
def rename_image_and_save(self, *args, **kwargs): | ||
if self.image: | ||
ext = Path(self.image.name).suffix | ||
unique_id = uuid.uuid4() | ||
self.image.name = f"{slugify(self.title)}-{unique_id}{ext}" | ||
super().save(*args, **kwargs) | ||
Comment on lines
+58
to
+63
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 method |
||
|
||
|
||
class MovieSession(models.Model): | ||
show_time = models.DateTimeField() | ||
|
@@ -57,15 +71,22 @@ class MovieSession(models.Model): | |
class Meta: | ||
ordering = ["-show_time"] | ||
|
||
def save(self, *args, **kwargs): | ||
if isinstance(self.show_time, str): | ||
self.show_time =\ | ||
datetime.strptime(self.show_time, "%Y-%m-%d %H:%M:%S") | ||
if timezone.is_naive(self.show_time): | ||
self.show_time = timezone.make_aware(self.show_time) | ||
super().save(*args, **kwargs) | ||
Comment on lines
+74
to
+80
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. ? don't need it here, the error with naive timezone is ok in this task |
||
|
||
def __str__(self): | ||
return self.movie.title + " " + str(self.show_time) | ||
|
||
|
||
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) | ||
|
@@ -78,9 +99,8 @@ 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" | ||
) | ||
order = models.ForeignKey(Order, on_delete=models.CASCADE, | ||
related_name="tickets") | ||
Comment on lines
+102
to
+103
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. we don't have problem with long line here, why changing it? |
||
row = models.IntegerField() | ||
seat = models.IntegerField() | ||
|
||
|
@@ -123,7 +143,8 @@ def save( | |
|
||
def __str__(self): | ||
return ( | ||
f"{str(self.movie_session)} (row: {self.row}, seat: {self.seat})" | ||
f"{str(self.movie_session)} " | ||
f"(row: {self.row}, seat: {self.seat})" | ||
) | ||
|
||
class Meta: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,13 +33,20 @@ class Meta: | |
class MovieSerializer(serializers.ModelSerializer): | ||
class Meta: | ||
model = Movie | ||
fields = ("id", "title", "description", "duration", "genres", "actors") | ||
fields = ( | ||
"id", | ||
"title", | ||
"description", | ||
"duration", | ||
"genres", | ||
"actors", | ||
"image", | ||
) | ||
|
||
|
||
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" | ||
) | ||
|
@@ -51,7 +58,14 @@ class MovieDetailSerializer(MovieSerializer): | |
|
||
class Meta: | ||
model = Movie | ||
fields = ("id", "title", "description", "duration", "genres", "actors") | ||
fields = ("id", "title", "description", "duration", | ||
"genres", "actors", "image") | ||
|
||
|
||
class MovieImageUploadSerializer(serializers.ModelSerializer): | ||
class Meta: | ||
model = Movie | ||
fields = ["id", "image"] | ||
|
||
|
||
class MovieSessionSerializer(serializers.ModelSerializer): | ||
|
@@ -62,9 +76,9 @@ 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 | ||
) | ||
movie_image = serializers.ImageField(source="movie.image", 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 | ||
) | ||
|
@@ -76,6 +90,7 @@ class Meta: | |
"id", | ||
"show_time", | ||
"movie_title", | ||
"movie_image", | ||
"cinema_hall_name", | ||
"cinema_hall_capacity", | ||
"tickets_available", | ||
|
@@ -85,9 +100,8 @@ 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"]) | ||
Comment on lines
+103
to
+104
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 data | ||
|
||
class Meta: | ||
|
@@ -106,11 +120,10 @@ class Meta: | |
|
||
|
||
class MovieSessionDetailSerializer(MovieSessionSerializer): | ||
movie = MovieListSerializer(many=False, read_only=True) | ||
movie = MovieDetailSerializer(many=False, read_only=True) | ||
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. In |
||
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 | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,11 @@ | ||
from datetime import datetime | ||
|
||
from rest_framework.views import APIView | ||
from rest_framework.response import Response | ||
from rest_framework.parsers import MultiPartParser, FormParser | ||
from rest_framework import status | ||
from django.shortcuts import get_object_or_404 | ||
|
||
from django.db.models import F, Count | ||
from rest_framework import viewsets, mixins | ||
from rest_framework.authentication import TokenAuthentication | ||
|
@@ -8,6 +14,7 @@ | |
from rest_framework.viewsets import GenericViewSet, ReadOnlyModelViewSet | ||
|
||
from cinema.models import Genre, Actor, CinemaHall, Movie, MovieSession, Order | ||
from .serializers import MovieImageUploadSerializer | ||
from cinema.permissions import IsAdminOrIfAuthenticatedReadOnly | ||
|
||
from cinema.serializers import ( | ||
|
@@ -103,6 +110,10 @@ def get_serializer_class(self): | |
|
||
return MovieSerializer | ||
|
||
def perform_create(self, serializer): | ||
"""Ensure the image field is not set during movie creation""" | ||
serializer.save(image=None) | ||
Comment on lines
+113
to
+115
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. it's better to write upload image here with action decorator
Comment on lines
+113
to
+115
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 |
||
|
||
|
||
class MovieSessionViewSet(viewsets.ModelViewSet): | ||
queryset = ( | ||
|
@@ -173,3 +184,17 @@ def get_serializer_class(self): | |
|
||
def perform_create(self, serializer): | ||
serializer.save(user=self.request.user) | ||
|
||
|
||
class MovieImageUploadView(APIView): | ||
parser_classes = [MultiPartParser, FormParser] | ||
|
||
def post(self, request, pk, *args, **kwargs): | ||
movie = get_object_or_404(Movie, pk=pk) | ||
serializer = MovieImageUploadSerializer(movie, | ||
data=request.data, | ||
partial=True) | ||
if serializer.is_valid(): | ||
serializer.save() | ||
Comment on lines
+197
to
+198
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. i guess it's better to use raise_exception=True here |
||
return Response(serializer.data, status=status.HTTP_200_OK) | ||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 =\ | ||
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. ? |
||
"django-insecure-6vubhk2$++agnctay_4pxy_8cq)mosmn(*-#2b^v4cgsh-^!i3" | ||
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
Comment on lines
+23
to
24
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 |
||
) | ||
|
||
# SECURITY WARNING: don't run with debug turned on in production! | ||
DEBUG = True | ||
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 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 |
||
|
@@ -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", | ||
}, | ||
] | ||
|
||
|
@@ -121,11 +120,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) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,22 @@ | ||
django==4.1 | ||
asgiref==3.8.1 | ||
black==24.10.0 | ||
click==8.1.7 | ||
colorama==0.4.6 | ||
Django==4.1 | ||
django-debug-toolbar==3.2.4 | ||
djangorestframework==3.13.1 | ||
flake8==5.0.4 | ||
flake8-quotes==3.3.1 | ||
flake8-variables-names==0.0.5 | ||
mccabe==0.7.0 | ||
mypy-extensions==1.0.0 | ||
packaging==24.2 | ||
pathspec==0.12.1 | ||
pep8-naming==0.13.2 | ||
django-debug-toolbar==3.2.4 | ||
djangorestframework==3.13.1 | ||
pillow==11.0.0 | ||
platformdirs==4.3.6 | ||
pycodestyle==2.9.1 | ||
pyflakes==2.5.0 | ||
pytz==2024.2 | ||
sqlparse==0.5.3 | ||
tzdata==2024.2 |
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.
The
image
field in theMovie
model is set toblank=True
but notnull=True
. This means that the database will require a value for this field, even if it's an empty string. If you want to allow the field to be completely optional, consider addingnull=True
.