Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
8 changes: 8 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.venv
__pycache__/
*.pyc
*.pyo
*.pyd
*.sqlite3
.git
.github
Comment on lines +1 to +8
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This file is missing some important entries. To keep the image secure and lightweight, you should also ignore:

  • Environment files (.env*): These may contain secrets and shouldn't be copied into the image.
  • IDE folders (.idea/, .vscode/): These are not needed for the application to run.
  • The media/ directory: Since this is handled by a volume, it shouldn't be part of the initial image build.

15 changes: 15 additions & 0 deletions .env.docker
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
DJANGO_SECRET_KEY=unsafe-secret-key
DJANGO_DEBUG=False

POSTGRES_DB=cinema
POSTGRES_USER=cinema_user
POSTGRES_PASSWORD=cinema_pass
POSTGRES_HOST=db
POSTGRES_PORT=5432

DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1

# Автоматичний суперкористувач
DJANGO_SUPERUSER_USERNAME=admin2
DJANGO_SUPERUSER_EMAIL=admin2@test.com
DJANGO_SUPERUSER_PASSWORD=Admin123!
10 changes: 10 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Django settings
DJANGO_SECRET_KEY=unsafe-secret-key
DJANGO_DEBUG=True

# PostgreSQL settings
POSTGRES_DB=cinema
POSTGRES_USER=cinema_user
POSTGRES_PASSWORD=cinema_pass
POSTGRES_HOST=db
POSTGRES_PORT=5432
10 changes: 10 additions & 0 deletions .env.local
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
DJANGO_SECRET_KEY=unsafe-secret-key
DJANGO_DEBUG=True

POSTGRES_DB=cinema
POSTGRES_USER=cinema_user
POSTGRES_PASSWORD=cinema_pass
POSTGRES_HOST=localhost
POSTGRES_PORT=5432

DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1
18 changes: 17 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,10 +1,26 @@
# IDE / Editor configs
.idea/
.vscode/
*.iml

# Environment files
.env
.env.local
.env.docker

# OS files
.DS_Store

# Virtual environments
venv/
.venv/

# Python cache
.pytest_cache/
**__pycache__/

# SQLite DB (якщо використовується для тестів)
**db.sqlite3
media

# Media files (зберігаються у volume)
media/
10 changes: 10 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FROM python:3.10-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

CMD ["gunicorn", "cinema.wsgi:application", "--bind", "0.0.0.0:8000"]
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The path to the WSGI application appears to be incorrect. Based on the project structure shown in cinema_service/settings.py and the command in your docker-compose.yml, it should likely be cinema_service.wsgi:application instead of cinema.wsgi:application.

17 changes: 17 additions & 0 deletions cinema/management/commands/wait_for_db.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import time
from django.db import connections
from django.db.utils import OperationalError
from django.core.management.base import BaseCommand


class Command(BaseCommand):
def handle(self, *args, **options):
self.stdout.write("Waiting for database...")
db_conn = None
while not db_conn:
try:
db_conn = connections["default"]
except OperationalError:
self.stdout.write("Database unavailable, waiting 1 second...")
time.sleep(1)
self.stdout.write(self.style.SUCCESS("Database available!"))
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Generated by Django 5.0.3 on 2026-03-18 16:25

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),
),
]
102 changes: 22 additions & 80 deletions cinema_service/settings.py
Original file line number Diff line number Diff line change
@@ -1,39 +1,15 @@
"""
Django settings for cinema_service project.

Generated by 'django-admin startproject' using Django 4.0.4.

For more information on this file, see
https://docs.djangoproject.com/en/4.0/topics/settings/

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

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent

SECRET_KEY = os.getenv("DJANGO_SECRET_KEY", "unsafe-secret-key")
DEBUG = os.getenv("DJANGO_DEBUG", "True") == "True"

# 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-6vubhk2$++agnctay_4pxy_8cq)mosmn(*-#2b^v4cgsh-^!i3"
)

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

ALLOWED_HOSTS = []

INTERNAL_IPS = [
"127.0.0.1",
]

# Application definition
INTERNAL_IPS = ["127.0.0.1"]

INSTALLED_APPS = [
"django.contrib.admin",
Expand Down Expand Up @@ -80,90 +56,56 @@

WSGI_APPLICATION = "cinema_service.wsgi.application"


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

DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
"ENGINE": "django.db.backends.postgresql",
"NAME": os.getenv("POSTGRES_DB", "cinema"),
"USER": os.getenv("POSTGRES_USER", "cinema_user"),
"PASSWORD": os.getenv("POSTGRES_PASSWORD", "cinema_pass"),
"HOST": os.getenv("POSTGRES_HOST", "db"),
"PORT": os.getenv("POSTGRES_PORT", "5432"),
}
}


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

AUTH_PASSWORD_VALIDATORS = [
{
"NAME": "django.contrib.auth.password_validation."
"UserAttributeSimilarityValidator",
},
{
"NAME": "django.contrib.auth.password_validation."
"MinimumLengthValidator",
},
{
"NAME": "django.contrib.auth.password_validation."
"CommonPasswordValidator",
},
{
"NAME": "django.contrib.auth.password_validation."
"NumericPasswordValidator",
},
{"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator"},
{"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"},
{"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"},
{"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"},
]

AUTH_USER_MODEL = "user.User"

# Internationalization
# https://docs.djangoproject.com/en/4.0/topics/i18n/

LANGUAGE_CODE = "en-us"

TIME_ZONE = "UTC"

USE_I18N = True
USE_TZ = True

USE_TZ = False


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.0/howto/static-files/

STATIC_URL = "static/"
STATIC_URL = "/static/"
STATIC_ROOT = os.getenv("DJANGO_STATIC_ROOT", BASE_DIR / "static")

MEDIA_URL = "/media/"
MEDIA_ROOT = BASE_DIR / "media"

# Default primary key field type
# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field
MEDIA_ROOT = os.getenv("DJANGO_MEDIA_ROOT", BASE_DIR / "media")

DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

REST_FRAMEWORK = {
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
"DEFAULT_AUTHENTICATION_CLASSES": (
"rest_framework_simplejwt.authentication.JWTAuthentication",
),
"DEFAULT_THROTTLE_CLASSES": [
"rest_framework.throttling.AnonRateThrottle",
"rest_framework.throttling.UserRateThrottle",
],
"DEFAULT_THROTTLE_RATES": {"anon": "10/day", "user": "30/day"},
"DEFAULT_AUTHENTICATION_CLASSES": (
"rest_framework_simplejwt.authentication.JWTAuthentication",
),
}

SPECTACULAR_SETTINGS = {
"TITLE": "Cinema Service API",
"DESCRIPTION": "Order cinema tickets",
"VERSION": "1.0.0",
"SERVE_INCLUDE_SCHEMA": False,
"SWAGGER_UI_SETTINGS": {
"deepLinking": True,
"defaultModelRendering": "model",
"defaultModelsExpandDepth": 2,
"defaultModelExpandDepth": 2,
},
}

SIMPLE_JWT = {
Expand Down
32 changes: 32 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
services:
db:
image: postgres:15
env_file:
- .env.docker
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"

app:
build: .
env_file:
- .env.docker
command: >
sh -c "python manage.py wait_for_db &&
python manage.py migrate &&
python manage.py collectstatic --noinput &&
gunicorn cinema_service.wsgi:application --bind 0.0.0.0:8000"
volumes:
- .:/app
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Using a bind mount .:/app for the application code is convenient for development but introduces issues. It mounts your entire project directory from the host into the container, which overrides all the files you added with COPY . . in your Dockerfile. This makes your .dockerignore file ineffective at runtime, as ignored files (like .git/ or .env files) will still be present inside the container. For a cleaner, more production-like setup, it's better to rely on the code that was built into the image.

- static_volume:/app/static
- media_volume:/app/media
ports:
- "8000:8000"
depends_on:
- db

volumes:
postgres_data:
static_volume:
media_volume:
Loading
Loading