diff --git a/.gitignore b/.gitignore index 9c18d6ac5..2b0a952fe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,380 @@ -.idea/ -.vscode/ -*.iml +# Created by https://www.toptal.com/developers/gitignore/api/python,pycharm,django +# Edit at https://www.toptal.com/developers/gitignore?templates=python,pycharm,django + +### Django ### +*.log +*.pot +*.pyc +__pycache__/ +local_settings.py +db.sqlite3 +db.sqlite3-journal +media + +# If your build process includes running collectstatic, then you probably don't need or want to include staticfiles/ +# in your Git repository. Update and uncomment the following line accordingly. +# /staticfiles/ + +### Django.Python Stack ### +# Byte-compiled / optimized / DLL files +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo + +# Django stuff: + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments .env -.DS_Store +.venv +env/ venv/ -.pytest_cache/ -**__pycache__/ -*.pyc -db.sqlite3 \ No newline at end of file +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +### PyCharm ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### PyCharm Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint +.idea/**/sonarlint/ + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator-enh.xml +.idea/**/markdown-navigator/ + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +.idea/$CACHE_FILE$ + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +.idea/codestream.xml + +# Azure Toolkit for IntelliJ plugin +# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij +.idea/**/azureSettings.xml + +### Python ### +# Byte-compiled / optimized / DLL files + +# C extensions + +# Distribution / packaging + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. + +# Installer logs + +# Unit test / coverage reports + +# Translations + +# Django stuff: + +# Flask stuff: + +# Scrapy stuff: + +# Sphinx documentation + +# PyBuilder + +# Jupyter Notebook + +# IPython + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm + +# Celery stuff + +# SageMath parsed files + +# Environments + +# Spyder project settings + +# Rope project settings + +# mkdocs documentation + +# mypy + +# Pyre type checker + +# pytype static type analyzer + +# Cython debug symbols + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. + +### Python Patch ### +# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration +poetry.toml + +# ruff +.ruff_cache/ + +# LSP config files +pyrightconfig.json + +# End of https://www.toptal.com/developers/gitignore/api/python,pycharm,django +.venv \ No newline at end of file diff --git a/cinema/admin.py b/cinema/admin.py index 06a2c4616..2234aea53 100644 --- a/cinema/admin.py +++ b/cinema/admin.py @@ -1,8 +1,13 @@ from django.contrib import admin -from cinema.models import Movie +from cinema.models import Movie, Genre, Actor, CinemaHall @admin.register(Movie) class MovieAdmin(admin.ModelAdmin): pass + + +admin.site.register(Genre) +admin.site.register(Actor) +admin.site.register(CinemaHall) diff --git a/cinema/migrations/0002_actor_cinemahall_genre_movie_actors_movie_genres.py b/cinema/migrations/0002_actor_cinemahall_genre_movie_actors_movie_genres.py new file mode 100644 index 000000000..0f8393a5b --- /dev/null +++ b/cinema/migrations/0002_actor_cinemahall_genre_movie_actors_movie_genres.py @@ -0,0 +1,71 @@ +# Generated by Django 4.1 on 2024-09-26 20:34 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("cinema", "0001_initial"), + ] + + operations = [ + migrations.CreateModel( + name="Actor", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("first_name", models.CharField(max_length=255)), + ("last_name", models.CharField(max_length=255)), + ], + ), + migrations.CreateModel( + name="CinemaHall", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=255)), + ("rows", models.IntegerField()), + ("seats_in_row", models.IntegerField()), + ], + ), + migrations.CreateModel( + name="Genre", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=255, unique=True)), + ], + ), + migrations.AddField( + model_name="movie", + name="actors", + field=models.ManyToManyField(related_name="movies", to="cinema.actor"), + ), + migrations.AddField( + model_name="movie", + name="genres", + field=models.ManyToManyField(related_name="movies", to="cinema.genre"), + ), + ] diff --git a/cinema/models.py b/cinema/models.py index cc477513f..838fc1b08 100644 --- a/cinema/models.py +++ b/cinema/models.py @@ -1,10 +1,33 @@ from django.db import models +class Actor(models.Model): + first_name = models.CharField(max_length=255) + last_name = models.CharField(max_length=255) + + +class Genre(models.Model): + name = models.CharField(max_length=255, unique=True) + + def __str__(self) -> str: + return self.name + + +class CinemaHall(models.Model): + name = models.CharField(max_length=255) + rows = models.IntegerField() + seats_in_row = models.IntegerField() + + def __str__(self) -> str: + return self.name + + class Movie(models.Model): title = models.CharField(max_length=255) description = models.TextField() duration = models.IntegerField() + actors = models.ManyToManyField(to=Actor, related_name="movies") + genres = models.ManyToManyField(to=Genre, related_name="movies") def __str__(self): return self.title diff --git a/cinema/serializers.py b/cinema/serializers.py index 050db5771..c108e68a4 100644 --- a/cinema/serializers.py +++ b/cinema/serializers.py @@ -1,6 +1,6 @@ from rest_framework import serializers -from cinema.models import Movie +from cinema.models import Movie, Actor, Genre, CinemaHall class MovieSerializer(serializers.Serializer): @@ -9,15 +9,78 @@ class MovieSerializer(serializers.Serializer): description = serializers.CharField() duration = serializers.IntegerField() - def create(self, validated_data): + def create(self, validated_data: dict) -> Movie: return Movie.objects.create(**validated_data) - def update(self, instance, validated_data): - instance.title = validated_data.get("title", instance.title) + def update(self, instance: Movie, validated_data: dict) -> Movie: + instance.title = validated_data.get( + "title", instance.title + ) instance.description = validated_data.get( "description", instance.description ) - instance.duration = validated_data.get("duration", instance.duration) + instance.duration = validated_data.get( + "duration", instance.duration + ) + + instance.save() + + return instance + + +class ActorSerializer(serializers.Serializer): + id = serializers.IntegerField(read_only=True) + first_name = serializers.CharField(max_length=255) + last_name = serializers.CharField(max_length=255) + + def create(self, validated_data: dict) -> Actor: + return Actor.objects.create(**validated_data) + + def update(self, instance: Actor, validated_data: dict) -> Actor: + instance.first_name = validated_data.get( + "first_name", + instance.first_name + ) + instance.last_name = validated_data.get( + "last_name", + instance.last_name + ) + + instance.save() + + return instance + + +class GenreSerializer(serializers.Serializer): + id = serializers.IntegerField(read_only=True) + name = serializers.CharField(max_length=255) + + def create(self, validated_data: dict) -> Genre: + return Genre.objects.create(**validated_data) + + def update(self, instance: Genre, validated_data: dict) -> Genre: + instance.name = validated_data.get("name", instance.name) + + instance.save() + + return instance + + +class CinemaHallSerializer(serializers.Serializer): + id = serializers.IntegerField(read_only=True) + name = serializers.CharField(max_length=255) + rows = serializers.IntegerField() + seats_in_row = serializers.IntegerField() + + def create(self, validated_data: dict) -> CinemaHall: + return CinemaHall.objects.create(**validated_data) + + def update(self, instance: CinemaHall, validated_data: dict) -> CinemaHall: + instance.name = validated_data.get("name", instance.name) + instance.rows = validated_data.get("rows", instance.rows) + instance.seats_in_row = validated_data.get( + "seats_in_row", instance.seats_in_row + ) instance.save() diff --git a/cinema/urls.py b/cinema/urls.py index 1ae7d5cb0..d995ef70d 100644 --- a/cinema/urls.py +++ b/cinema/urls.py @@ -1,10 +1,39 @@ -from django.urls import path +from django.urls import path, include +from rest_framework import routers -from cinema.views import movie_list, movie_detail +from .views import GenreList, GenreDetail, ActorDetail +from .views.actor_views import ActorList +from .views.cinema_hall_views import CinemaHallViewSet +from .views.movie_views import MovieViewSet + +router = routers.DefaultRouter() +router.register("movies", MovieViewSet) +cinema_hall_list = CinemaHallViewSet.as_view( + actions={ + "get": "list", + "post": "create"} +) +cinema_hall_detail = CinemaHallViewSet.as_view( + actions={ + "get": "retrieve", + "put": "update", + "patch": "partial_update", + "delete": "destroy", + } +) urlpatterns = [ - path("movies/", movie_list, name="movie-list"), - path("movies//", movie_detail, name="movie-detail"), + path("genres/", GenreList.as_view(), name="genre-list"), + path("genres//", GenreDetail.as_view(), name="genre-detail"), + path("actors/", ActorList.as_view(), name="actor-list"), + path("actors//", ActorDetail.as_view(), name="actor-detail"), + path("cinema_halls/", cinema_hall_list, name="cinema-hall-list"), + path( + "cinema_halls//", + cinema_hall_detail, + name="cinema-hall-detail" + ), + path("", include(router.urls)), ] app_name = "cinema" diff --git a/cinema/views.py b/cinema/views.py deleted file mode 100644 index 78ba8a79c..000000000 --- a/cinema/views.py +++ /dev/null @@ -1,45 +0,0 @@ -from rest_framework.decorators import api_view -from rest_framework.response import Response -from rest_framework import status - -from django.shortcuts import get_object_or_404 - -from cinema.models import Movie -from cinema.serializers import MovieSerializer - - -@api_view(["GET", "POST"]) -def movie_list(request): - if request.method == "GET": - movies = Movie.objects.all() - serializer = MovieSerializer(movies, many=True) - return Response(serializer.data, status=status.HTTP_200_OK) - - if request.method == "POST": - serializer = MovieSerializer(data=request.data) - if serializer.is_valid(): - serializer.save() - return Response(serializer.data, status=status.HTTP_201_CREATED) - - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - - -@api_view(["GET", "PUT", "DELETE"]) -def movie_detail(request, pk): - movie = get_object_or_404(Movie, pk=pk) - - if request.method == "GET": - serializer = MovieSerializer(movie) - return Response(serializer.data, status=status.HTTP_200_OK) - - if request.method == "PUT": - serializer = MovieSerializer(movie, data=request.data) - if serializer.is_valid(): - serializer.save() - return Response(serializer.data, status=status.HTTP_200_OK) - - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - - if request.method == "DELETE": - movie.delete() - return Response(status=status.HTTP_204_NO_CONTENT) diff --git a/cinema/views/__init__.py b/cinema/views/__init__.py new file mode 100644 index 000000000..7b22e47c2 --- /dev/null +++ b/cinema/views/__init__.py @@ -0,0 +1,4 @@ +from .genre_views import GenreList, GenreDetail +from .actor_views import ActorList, ActorDetail +from .cinema_hall_views import CinemaHallViewSet +from .movie_views import MovieViewSet diff --git a/cinema/views/actor_views.py b/cinema/views/actor_views.py new file mode 100644 index 000000000..08a63bd14 --- /dev/null +++ b/cinema/views/actor_views.py @@ -0,0 +1,41 @@ +from rest_framework import generics, mixins +from rest_framework.request import Request +from rest_framework.response import Response + +from cinema.models import Actor +from cinema.serializers import ActorSerializer + + +class ActorList( + generics.GenericAPIView, mixins.ListModelMixin, mixins.CreateModelMixin +): + queryset = Actor.objects.all() + serializer_class = ActorSerializer + + def get(self, request: Request, *args, **kwargs) -> Response: + return self.list(request, *args, **kwargs) + + def post(self, request: Request, *args, **kwargs): + return self.create(request, *args, **kwargs) + + +class ActorDetail( + generics.GenericAPIView, + mixins.RetrieveModelMixin, + mixins.UpdateModelMixin, + mixins.DestroyModelMixin, +): + queryset = Actor.objects.all() + serializer_class = ActorSerializer + + def get(self, request: Request, *args, **kwargs) -> Response: + return self.retrieve(request, *args, **kwargs) + + def put(self, request: Request, *args, **kwargs) -> Response: + return self.update(request, *args, **kwargs) + + def patch(self, request: Request, *args, **kwargs) -> Response: + return self.partial_update(request, *args, **kwargs) + + def delete(self, request: Request, *args, **kwargs) -> Response: + return self.destroy(request, *args, **kwargs) diff --git a/cinema/views/cinema_hall_views.py b/cinema/views/cinema_hall_views.py new file mode 100644 index 000000000..c4fb5311f --- /dev/null +++ b/cinema/views/cinema_hall_views.py @@ -0,0 +1,16 @@ +from rest_framework import viewsets, mixins + +from cinema.models import CinemaHall +from cinema.serializers import CinemaHallSerializer + + +class CinemaHallViewSet( + viewsets.GenericViewSet, + mixins.ListModelMixin, + mixins.CreateModelMixin, + mixins.RetrieveModelMixin, + mixins.UpdateModelMixin, + mixins.DestroyModelMixin, +): + queryset = CinemaHall.objects.all() + serializer_class = CinemaHallSerializer diff --git a/cinema/views/genre_views.py b/cinema/views/genre_views.py new file mode 100644 index 000000000..38a93c8f9 --- /dev/null +++ b/cinema/views/genre_views.py @@ -0,0 +1,48 @@ +from django.shortcuts import get_object_or_404 +from rest_framework import status +from rest_framework.request import Request +from rest_framework.response import Response +from rest_framework.views import APIView + +from cinema.models import Genre +from cinema.serializers import GenreSerializer + + +class GenreList(APIView): + def get(self, request: Request) -> Response: + genre = Genre.objects.all() + serializer = GenreSerializer(genre, many=True) + return Response(serializer.data, status=status.HTTP_200_OK) + + def post(self, request: Request) -> Response: + serializer = GenreSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + serializer.save() + return Response(serializer.data, status=status.HTTP_201_CREATED) + + +class GenreDetail(APIView): + def get_object(self, pk: int) -> Genre: + return get_object_or_404(Genre, pk=pk) + + def get(self, request: Request, pk: int) -> Response: + serializer = GenreSerializer(self.get_object(pk=pk)) + return Response(serializer.data, status=status.HTTP_200_OK) + + def put(self, request: Request, pk: int) -> Response: + serializer = GenreSerializer(self.get_object(pk=pk), data=request.data) + serializer.is_valid(raise_exception=True) + serializer.save() + return Response(serializer.data, status=status.HTTP_200_OK) + + def patch(self, request: Request, pk: int) -> Response: + serializer = GenreSerializer( + self.get_object(pk=pk), data=request.data, partial=True + ) + serializer.is_valid(raise_exception=True) + serializer.save() + return Response(serializer.data, status=status.HTTP_200_OK) + + def delete(self, request: Request, pk: int) -> Response: + self.get_object(pk=pk).delete() + return Response(status=status.HTTP_204_NO_CONTENT) diff --git a/cinema/views/movie_views.py b/cinema/views/movie_views.py new file mode 100644 index 000000000..79f576a16 --- /dev/null +++ b/cinema/views/movie_views.py @@ -0,0 +1,9 @@ +from rest_framework import viewsets + +from cinema.models import Movie +from cinema.serializers import MovieSerializer + + +class MovieViewSet(viewsets.ModelViewSet): + queryset = Movie.objects.all() + serializer_class = MovieSerializer diff --git a/cinema_service/settings.py b/cinema_service/settings.py index def98786c..7a7c6cebe 100644 --- a/cinema_service/settings.py +++ b/cinema_service/settings.py @@ -15,13 +15,13 @@ # 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/ # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = ( - "django-insecure-ru6ndfka@95_(lysua8yhdjq@vpiqgv3yru4r)q3h4_u8x7dfy" + "django-insecure-ru6ndfka@95_" + "(lysua8yhdjq@vpiqgv3yru4r)q3h4_u8x7dfy" ) # SECURITY WARNING: don't run with debug turned on in production! @@ -29,7 +29,6 @@ ALLOWED_HOSTS = [] - # Application definition INSTALLED_APPS = [ @@ -73,7 +72,6 @@ WSGI_APPLICATION = "cinema_service.wsgi.application" - # Database # https://docs.djangoproject.com/en/4.0/ref/settings/#databases @@ -84,30 +82,28 @@ } } - # 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", }, ] - # Internationalization # https://docs.djangoproject.com/en/4.0/topics/i18n/ @@ -119,7 +115,6 @@ USE_TZ = True - # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/4.0/howto/static-files/