diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..45e93965 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +/.venv/ +/.idea/ +/Dockerfile +/docker-compose.yaml diff --git a/.flake8 b/.flake8 index 364ad762..11cb3b69 100644 --- a/.flake8 +++ b/.flake8 @@ -1,6 +1,6 @@ [flake8] inline-quotes = " -ignore = E203, E266, W503, N807, N818, F401 +ignore = E203, E266, W503, N807, N818, F401, VNE003 max-line-length = 79 max-complexity = 18 select = B,C,E,F,W,T4,B9,Q0,N8,VNE diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..35753bb9 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +FROM python:3.12.1-alpine3.18 +LABEL maintainer="knyrikkolesnichenko2004@gmail.com" + +WORKDIR app/ + +ENV PYTHONBUFFERED 1 + +COPY requirements.txt requirements.txt +RUN pip install -r requirements.txt + +COPY . . + +RUN mkdir -p /files/media/ + +RUN adduser \ + --disabled-password \ + --no-create-home \ + django_user + +RUN chown -R django_user /files/media +RUN chmod -R 755 /files/media + +USER django_user diff --git a/cinema/management/__init__.py b/cinema/management/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/cinema/management/commands/__init__.py b/cinema/management/commands/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/cinema/management/commands/wait_for_db.py b/cinema/management/commands/wait_for_db.py new file mode 100644 index 00000000..3b4a555d --- /dev/null +++ b/cinema/management/commands/wait_for_db.py @@ -0,0 +1,27 @@ +import time + +from django.core.management.base import BaseCommand +from django.db.utils import OperationalError +from psycopg import OperationalError as PsycopgOperationalError + + +class Command(BaseCommand): + help = "Force the app to wait while the database is unavailable" + + def handle(self, *args, **options) -> None: + self.stdout.write("Waiting for the database...") + db_up = False + + while not db_up: + try: + self.check(databases=("default",)) + db_up = True + except (PsycopgOperationalError, OperationalError): + self.stdout.write( + "Database is unavailable, wait for a second..." + ) + time.sleep(1) + + self.stdout.write( + self.style.SUCCESS("The database is available!") + ) diff --git a/cinema/migrations/0002_alter_movie_actors_alter_movie_genres_and_more.py b/cinema/migrations/0002_alter_movie_actors_alter_movie_genres_and_more.py new file mode 100644 index 00000000..ffa59d8d --- /dev/null +++ b/cinema/migrations/0002_alter_movie_actors_alter_movie_genres_and_more.py @@ -0,0 +1,41 @@ +# Generated by Django 6.0.4 on 2026-04-25 06:03 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cinema', '0001_initial'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AlterField( + model_name='movie', + name='actors', + field=models.ManyToManyField(blank=True, related_name='movies', to='cinema.actor'), + ), + migrations.AlterField( + model_name='movie', + name='genres', + field=models.ManyToManyField(blank=True, related_name='movies', to='cinema.genre'), + ), + migrations.AlterField( + model_name='moviesession', + name='cinema_hall', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='movie_sessions', to='cinema.cinemahall'), + ), + migrations.AlterField( + model_name='moviesession', + name='movie', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='movie_sessions', to='cinema.movie'), + ), + migrations.AlterField( + model_name='order', + name='user', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='orders', to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/cinema_service/settings.py b/cinema_service/settings.py index 90dde772..7b5b1c23 100644 --- a/cinema_service/settings.py +++ b/cinema_service/settings.py @@ -9,6 +9,7 @@ For the full list of settings and their values, see https://docs.djangoproject.com/en/4.0/ref/settings/ """ +import os from datetime import timedelta from pathlib import Path @@ -86,9 +87,13 @@ DATABASES = { "default": { - "ENGINE": "django.db.backends.sqlite3", - "NAME": BASE_DIR / "db.sqlite3", - } + "ENGINE": "django.db.backends.postgresql", + "NAME": os.environ.get("POSTGRES_DB"), + "USER": os.environ.get("POSTGRES_USER"), + "PASSWORD": os.environ.get("POSTGRES_PASSWORD"), + "HOST": os.environ.get("POSTGRES_HOST"), + "PORT": os.environ.get("POSTGRES_PORT"), + }, } @@ -134,7 +139,7 @@ STATIC_URL = "static/" MEDIA_URL = "/media/" -MEDIA_ROOT = BASE_DIR / "media" +MEDIA_ROOT = "/files/media" # Default primary key field type # https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 00000000..a5e68a6a --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,31 @@ +services: + cinema: + build: + context: . + env_file: + - .env + ports: + - "8000:8000" + volumes: + - my_media:/files/media + command: > + sh -c "python manage.py wait_for_db && + python manage.py makemigrations && + python manage.py migrate && + python manage.py runserver 0.0.0.0:8000" + depends_on: + - db + + db: + image: postgres:16.0-alpine3.17 + restart: always + ports: + - "5432:5432" + env_file: + - .env + volumes: + - my_db:$PGDATA + +volumes: + my_db: + my_media: diff --git a/requirements.txt b/requirements.txt index 2dc12c67..96355c19 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,3 +8,5 @@ djangorestframework djangorestframework-simplejwt drf-spectacular Pillow +psycopg +psycopg_binary