From 894bb13663e92261e3bbb8533c2421118fcb2bfc Mon Sep 17 00:00:00 2001 From: Erik Agayan Date: Thu, 6 Jul 2023 16:03:54 +0300 Subject: [PATCH 1/3] Solution #1 --- ...ovie_db_movie_title_5d0841_idx_and_more.py | 94 +++++++++++++++++++ db/models.py | 77 +++++++++++++++ services/movie.py | 24 +++-- services/movie_session.py | 7 +- services/order.py | 37 ++++++++ services/user.py | 48 ++++++++++ settings.py | 4 + 7 files changed, 282 insertions(+), 9 deletions(-) create mode 100644 db/migrations/0002_user_order_ticket_movie_db_movie_title_5d0841_idx_and_more.py create mode 100644 services/order.py create mode 100644 services/user.py diff --git a/db/migrations/0002_user_order_ticket_movie_db_movie_title_5d0841_idx_and_more.py b/db/migrations/0002_user_order_ticket_movie_db_movie_title_5d0841_idx_and_more.py new file mode 100644 index 000000000..42b282867 --- /dev/null +++ b/db/migrations/0002_user_order_ticket_movie_db_movie_title_5d0841_idx_and_more.py @@ -0,0 +1,94 @@ +# Generated by Django 4.0.2 on 2023-07-06 15:27 + +from django.conf import settings +import django.contrib.auth.models +import django.contrib.auth.validators +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('auth', '0012_alter_user_first_name_max_length'), + ('db', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='User', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('password', models.CharField(max_length=128, verbose_name='password')), + ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), + ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), + ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), + ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), + ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), + ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')), + ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), + ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), + ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), + ], + options={ + 'verbose_name': 'user', + 'verbose_name_plural': 'users', + 'abstract': False, + }, + managers=[ + ('objects', django.contrib.auth.models.UserManager()), + ], + ), + migrations.CreateModel( + name='Order', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ], + options={ + 'ordering': ['-created_at'], + }, + ), + migrations.CreateModel( + name='Ticket', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('row', models.IntegerField()), + ('seat', models.IntegerField()), + ], + ), + migrations.AddIndex( + model_name='movie', + index=models.Index(fields=['title'], name='db_movie_title_5d0841_idx'), + ), + migrations.AddField( + model_name='ticket', + name='movie_session', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='db.moviesession'), + ), + migrations.AddField( + model_name='ticket', + name='order', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='db.order'), + ), + migrations.AddField( + model_name='order', + name='user', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='user', + name='groups', + field=models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups'), + ), + migrations.AddField( + model_name='user', + name='user_permissions', + field=models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions'), + ), + migrations.AddConstraint( + model_name='ticket', + constraint=models.UniqueConstraint(fields=('row', 'seat', 'movie_session'), name='unique_ticket_row_seat_movie_session'), + ), + ] diff --git a/db/models.py b/db/models.py index 584ef55df..1481aa1ee 100644 --- a/db/models.py +++ b/db/models.py @@ -1,4 +1,8 @@ from django.db import models +from django.core.exceptions import ValidationError +from django.contrib.auth.models import AbstractUser + +import settings class Genre(models.Model): @@ -22,6 +26,11 @@ class Movie(models.Model): actors = models.ManyToManyField(to=Actor) genres = models.ManyToManyField(to=Genre) + class Meta: + indexes = [ + models.Index(fields=["title"]) + ] + def __str__(self) -> str: return self.title @@ -46,3 +55,71 @@ class MovieSession(models.Model): def __str__(self) -> str: return f"{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) + + class Meta: + ordering = ["-created_at"] + + def __str__(self) -> str: + return str(self.created_at) + + +class Ticket(models.Model): + movie_session = models.ForeignKey(MovieSession, on_delete=models.CASCADE) + order = models.ForeignKey(Order, on_delete=models.CASCADE) + row = models.IntegerField() + seat = models.IntegerField() + + class Meta: + constraints = [ + models.UniqueConstraint( + fields=["row", "seat", "movie_session"], + name="unique_ticket_row_seat_movie_session" + ) + ] + + def clean(self) -> None: + if not self.row <= self.movie_session.cinema_hall.rows: + raise ValidationError( + { + "row": [ + "row number must be in available range: (1, rows): " + f"(1, {self.movie_session.cinema_hall.rows})" + ] + } + ) + + if not self.seat <= self.movie_session.cinema_hall.seats_in_row: + raise ValidationError( + { + "seat": [ + "seat number must be in available range: " + "(1, seats_in_row): " + f"(1, {self.movie_session.cinema_hall.seats_in_row})" + ] + } + ) + + def save( + self, + force_insert: bool = False, + force_update: bool = False, + using: str = None, + update_fields: list[str] = None + ) -> None: + self.full_clean() + return super().save(force_insert, force_update, using, update_fields) + + def __str__(self) -> str: + return (f"{self.movie_session.movie.title} " + f"{self.movie_session.show_time} " + f"(row: {self.row}, seat: {self.seat})") + + +class User(AbstractUser): + pass diff --git a/services/movie.py b/services/movie.py index 4d0f63237..26e83f160 100644 --- a/services/movie.py +++ b/services/movie.py @@ -1,4 +1,5 @@ from django.db.models import QuerySet +from django.db import transaction from db.models import Movie @@ -6,9 +7,13 @@ def get_movies( genres_ids: list[int] = None, actors_ids: list[int] = None, + title: str = None ) -> QuerySet: queryset = Movie.objects.all() + if title: + queryset = queryset.filter(title__icontains=title) + if genres_ids: queryset = queryset.filter(genres__id__in=genres_ids) @@ -28,13 +33,16 @@ def create_movie( genres_ids: list = None, actors_ids: list = None, ) -> Movie: - movie = Movie.objects.create( - title=movie_title, - description=movie_description, - ) - if genres_ids: - movie.genres.set(genres_ids) - if actors_ids: - movie.actors.set(actors_ids) + with transaction.atomic(): + movie = Movie.objects.create( + title=movie_title, + description=movie_description, + ) + + if genres_ids: + movie.genres.set(genres_ids) + + if actors_ids: + movie.actors.set(actors_ids) return movie diff --git a/services/movie_session.py b/services/movie_session.py index f326a082e..f209c07d0 100644 --- a/services/movie_session.py +++ b/services/movie_session.py @@ -1,6 +1,6 @@ from django.db.models import QuerySet -from db.models import MovieSession +from db.models import MovieSession, Ticket def create_movie_session( @@ -42,3 +42,8 @@ def update_movie_session( def delete_movie_session_by_id(session_id: int) -> None: MovieSession.objects.get(id=session_id).delete() + + +def get_taken_seats(movie_session_id: int) -> list[dict]: + return list(Ticket.objects.filter( + movie_session_id=movie_session_id).values("row", "seat")) diff --git a/services/order.py b/services/order.py new file mode 100644 index 000000000..3c6f01261 --- /dev/null +++ b/services/order.py @@ -0,0 +1,37 @@ +from django.contrib.auth import get_user_model +from db.models import Order, Ticket +from django.db import transaction +from django.db.models import QuerySet + + +def create_order( + tickets: list[dict], + username: str, + date: str = None) -> Order: + with transaction.atomic(): + user = get_user_model().objects.get(username=username) + order = Order.objects.create(user=user) + + if date: + order.created_at = date + order.save() + + for ticket in tickets: + Ticket.objects.create( + movie_session_id=ticket["movie_session"], + order_id=order.id, + row=ticket["row"], + seat=ticket["seat"] + ) + order.save() + + return order + + +def get_orders(username: str = None) -> QuerySet[Order]: + order = Order.objects.all() + + if username: + order = Order.objects.filter(user__username=username) + + return order diff --git a/services/user.py b/services/user.py new file mode 100644 index 000000000..103aba79c --- /dev/null +++ b/services/user.py @@ -0,0 +1,48 @@ +from django.contrib.auth import get_user_model +from db.models import User + + +def create_user( + username: str, + password: str, + email: str = "", + first_name: str = "", + last_name: str = "" +) -> User: + return get_user_model().objects.create_user( + username=username, + password=password, + email=email, + first_name=first_name, + last_name=last_name + ) + + +def get_user(user_id: int) -> User: + return get_user_model().objects.get(id=user_id) + + +def update_user(user_id: int, + username: str = None, + password: str = None, + email: str = None, + first_name: str = None, + last_name: str = None) -> None: + user = get_user_model().objects.get(id=user_id) + + if username: + user.username = username + + if password: + user.set_password(password) + + if email: + user.email = email + + if first_name: + user.first_name = first_name + + if last_name: + user.last_name = last_name + + user.save() diff --git a/settings.py b/settings.py index f25910b30..6dfeee6b1 100644 --- a/settings.py +++ b/settings.py @@ -25,4 +25,8 @@ INSTALLED_APPS = [ "db", + "django.contrib.auth", + "django.contrib.contenttypes", ] + +AUTH_USER_MODEL = "db.User" From 1df9a3eca290ceed93b43b6671dd1f460315ce31 Mon Sep 17 00:00:00 2001 From: Erik Agayan Date: Fri, 7 Jul 2023 11:25:43 +0300 Subject: [PATCH 2/3] Solution #2 --- db/models.py | 4 ++-- services/order.py | 1 - services/user.py | 28 ++++++++++++++++++---------- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/db/models.py b/db/models.py index 1481aa1ee..7b2685b5c 100644 --- a/db/models.py +++ b/db/models.py @@ -84,7 +84,7 @@ class Meta: ] def clean(self) -> None: - if not self.row <= self.movie_session.cinema_hall.rows: + if not 1 <= self.row <= self.movie_session.cinema_hall.rows: raise ValidationError( { "row": [ @@ -94,7 +94,7 @@ def clean(self) -> None: } ) - if not self.seat <= self.movie_session.cinema_hall.seats_in_row: + if not 1 <= self.seat <= self.movie_session.cinema_hall.seats_in_row: raise ValidationError( { "seat": [ diff --git a/services/order.py b/services/order.py index 3c6f01261..ae1d2983a 100644 --- a/services/order.py +++ b/services/order.py @@ -14,7 +14,6 @@ def create_order( if date: order.created_at = date - order.save() for ticket in tickets: Ticket.objects.create( diff --git a/services/user.py b/services/user.py index 103aba79c..a034e25d1 100644 --- a/services/user.py +++ b/services/user.py @@ -5,17 +5,25 @@ def create_user( username: str, password: str, - email: str = "", - first_name: str = "", - last_name: str = "" + email: str = None, + first_name: str = None, + last_name: str = None ) -> User: - return get_user_model().objects.create_user( - username=username, - password=password, - email=email, - first_name=first_name, - last_name=last_name - ) + user_data = { + "username": username, + "password": password, + } + + if email: + user_data["email"] = email + + if first_name: + user_data["first_name"] = first_name + + if last_name: + user_data["last_name"] = last_name + + return get_user_model().objects.create_user(**user_data) def get_user(user_id: int) -> User: From 7a150f3361a4f38ee013aace330d5081dd737076 Mon Sep 17 00:00:00 2001 From: Erik Agayan Date: Mon, 10 Jul 2023 22:27:55 +0300 Subject: [PATCH 3/3] Solution #3 --- services/movie_session.py | 3 ++- services/order.py | 5 +++-- services/user.py | 42 ++++++++++++--------------------------- 3 files changed, 18 insertions(+), 32 deletions(-) diff --git a/services/movie_session.py b/services/movie_session.py index f209c07d0..9b4b49f05 100644 --- a/services/movie_session.py +++ b/services/movie_session.py @@ -46,4 +46,5 @@ def delete_movie_session_by_id(session_id: int) -> None: def get_taken_seats(movie_session_id: int) -> list[dict]: return list(Ticket.objects.filter( - movie_session_id=movie_session_id).values("row", "seat")) + movie_session_id=movie_session_id + ).values("row", "seat")) diff --git a/services/order.py b/services/order.py index ae1d2983a..07419a315 100644 --- a/services/order.py +++ b/services/order.py @@ -7,13 +7,15 @@ def create_order( tickets: list[dict], username: str, - date: str = None) -> Order: + date: str = None +) -> Order: with transaction.atomic(): user = get_user_model().objects.get(username=username) order = Order.objects.create(user=user) if date: order.created_at = date + order.save() for ticket in tickets: Ticket.objects.create( @@ -22,7 +24,6 @@ def create_order( row=ticket["row"], seat=ticket["seat"] ) - order.save() return order diff --git a/services/user.py b/services/user.py index a034e25d1..d02c026fa 100644 --- a/services/user.py +++ b/services/user.py @@ -5,52 +5,36 @@ def create_user( username: str, password: str, - email: str = None, - first_name: str = None, - last_name: str = None + **kwargs ) -> User: user_data = { "username": username, "password": password, } - if email: - user_data["email"] = email + user = get_user_model().objects.create_user(**user_data) - if first_name: - user_data["first_name"] = first_name + for key, value in kwargs.items(): + if value is not None and value != "": + setattr(user, key, value) - if last_name: - user_data["last_name"] = last_name + user.save() - return get_user_model().objects.create_user(**user_data) + return user def get_user(user_id: int) -> User: return get_user_model().objects.get(id=user_id) -def update_user(user_id: int, - username: str = None, - password: str = None, - email: str = None, - first_name: str = None, - last_name: str = None) -> None: +def update_user(user_id: int, **kwargs) -> None: user = get_user_model().objects.get(id=user_id) - if username: - user.username = username - - if password: - user.set_password(password) - - if email: - user.email = email - - if first_name: - user.first_name = first_name + for key, value in kwargs.items(): + if value is not None and value != "": + setattr(user, key, value) - if last_name: - user.last_name = last_name + if "password" in kwargs and kwargs["password"]: + user.set_password(kwargs["password"]) user.save()