Структура проєкту Django¶
Стандартна ієрархія файлів¶
my_project/ # Коренева директорія (Контейнер всього проєкту)
├── manage.py # ⚙️ Оркестратор командного рядка
├── my_project/ # 🛠 ГОЛОВНИЙ ПАКЕТ КОНФІГУРАЦІЇ (System Level)
│ ├── __init__.py
│ ├── settings.py # 🎛 Центральний хаб налаштувань
│ ├── urls.py # 🔀 Головний маршрутизатор (Root URLconf)
│ ├── asgi.py # ⚡️ Асинхронний шлюз (WebSockets / Async)
│ └── wsgi.py # 🌐 Синхронний шлюз (HTTP)
├── my_app/ # 📦 МОДУЛЬНИЙ ДОДАТОК (App Level)
│ ├── __init__.py
│ ├── admin.py # 🛡 Конфігурація бек-офісу
│ ├── apps.py # 📝 Реєстрація додатку
│ ├── models.py # 🗄 Схема бази даних (Data Layer)
│ ├── views.py # 🧠 HTTP-оркестратор (делегує в services/selectors)
│ ├── urls.py # 🔀 Маршрути додатку
│ ├── forms.py # 📋 Django Forms / ModelForms
│ ├── tests.py # ✅ Тести
│ └── migrations/ # 🕰 Історія еволюції БД
│ ├── __init__.py
│ └── 0001_initial.py
├── templates/ # 🎨 ГЛОБАЛЬНІ ШАБЛОНИ (Presentation Layer)
│ └── base.html
└── static/ # 🖼 СТАТИЧНІ РЕСУРСИ
├── css/
├── js/
└── images/
Архітектура та потік виконання¶
Високорівнева архітектура бекенду¶
graph TD
Client[Браузер / Клієнт] -->|HTTP запит| Nginx[Nginx Зворотний проксі]
subgraph Роздача статики
Nginx -->|Статика/Медіа| Static[static/ та media/]
end
subgraph Django Application Server
Nginx -->|Динамічний трафік| Gateway[wsgi.py / asgi.py]
Gateway --> Middleware[Стек Middleware]
Middleware --> Router[urls.py]
Router -->|Збіг маршруту| View[views.py]
View <-->|ORM запити| Model[models.py]
View -->|Передача контексту| Template[templates/]
end
subgraph Сховище даних
Model <-->|SQL| DB[(База даних)]
end
Життєвий цикл HTTP-запиту¶
sequenceDiagram
participant Nginx as Nginx
participant Web as wsgi.py / asgi.py
participant Route as urls.py
participant View as views.py
participant DB as models.py (ORM)
participant HTML as templates/
Nginx->>Web: Вхідний HTTP-запит
Web->>Route: Передача диспетчеру
Route->>View: Виклик відповідної функції
View->>DB: Book.objects.all()
DB-->>View: QuerySet (дані)
View->>HTML: render(request, 'books.html', data)
HTML-->>View: Скомпільований HTML-рядок
View-->>Web: HttpResponse
Web-->>Nginx: Байти відповіді
Детальний розбір компонентів¶
Системний рівень — Оркестрування¶
manage.py¶
| Аспект | Опис |
|---|---|
| Призначення | Оркестратор командного рядка для всього проєкту |
| Внутрішня роль | Встановлює DJANGO_SETTINGS_MODULE та виконує команди Django |
| У lifecycle запиту | Жодної участі — не обробляє живий вебтрафік |
| У CI/CD | Запускає тести, збирає статику, застосовує міграції |
Ментальна модель: Виконроб на будівництві — ти віддаєш йому накази, він делегує фреймворку.
Помилка: Думати, що
manage.pyзапускає продакшен-сервер. У продакшені цей файл ігнорується при обробці трафіку.
settings.py¶
| Аспект | Опис |
|---|---|
| Призначення | Центральний конфігураційний хаб |
| Внутрішня роль | Python-модуль зі глобальними змінними (БД, встановлені додатки, ключі безпеки) |
| У lifecycle запиту | Завантажується в пам'ять один раз під час старту сервера |
| Зовнішні сервіси | PostgreSQL, Redis, AWS S3, Celery — все підключається тут |
Ментальна модель: Головний електрощит і панель управління всією будівлею.
# Критичні налаштування
INSTALLED_APPS = ['django.contrib.auth', 'myapp']
DATABASES = {'default': {'ENGINE': 'django.db.backends.postgresql', ...}}
MIDDLEWARE = ['django.middleware.security.SecurityMiddleware', ...]
Помилка: Намагатися динамічно змінювати
settings.pyдля різних користувачів під час роботи. Налаштування — глобальні та статичні.
wsgi.py та asgi.py¶
| Аспект | Опис |
|---|---|
| Призначення | Точки входу для серверів додатків (Gunicorn, Uvicorn) |
| Внутрішня роль | Надає стандартний об'єкт application для зв'язку з Django |
| У lifecycle запиту | Перший рядок Django-коду при надходженні HTTP-запиту |
wsgi.py |
Синхронний протокол — традиційні HTTP-запити |
asgi.py |
Асинхронний протокол — WebSockets, Server-Sent Events |
Ментальна модель: Стіл перекладача — перетворює "сирі" мережеві HTTP-байти в Python-словники.
Помилка: Думати, що Django самостійно слухає Інтернет. Трафік завжди спочатку проходить через WSGI/ASGI сервер.
Рівень додатку — Логіка та маршрутизація¶
urls.py¶
| Аспект | Опис |
|---|---|
| Призначення | Диспетчер URL-адрес |
| Внутрішня роль | Список urlpatterns — зіставлення URL-рядків з Python-функціями |
| Алгоритм | Пошук зверху вниз, зупиняється на першому збігу |
| Для API | Визначає контракт ендпоінтів |
Ментальна модель: Сортувальник пошти — читає адресу та віддає конверт потрібному кур'єру.
Помилка: Очікувати, що Django знайде "найкращий" збіг. Він бере перший — статичні маршрути мають стояти вище динамічних.
views.py¶
| Аспект | Опис |
|---|---|
| Призначення | HTTP-оркестратор — тонкий шар між мережею і бізнес-доменом |
| Внутрішня роль | Приймає HttpRequest, викликає serializer → service/selector, повертає HttpResponse |
| У lifecycle запиту | Парсить запит, делегує логіку, форматує відповідь |
| Може повертати | HTML, JSON, PDF, CSV, Redirect |
Ментальна модель: Диспетчер — приймає дзвінок, з'єднує з потрібним фахівцем і передає відповідь. Сам нічого не вирішує.
# View у clean architecture — максимально тонка
class FloodEventCreateAPIView(APIView):
def post(self, request):
serializer = FloodEventInputSerializer(data=request.data) # валідація
serializer.is_valid(raise_exception=True)
event = flood_event_create(**serializer.validated_data) # бізнес-логіка у service
return Response(FloodEventOutputSerializer(event).data, status=201)
| View НЕ повинна | View ПОВИННА |
|---|---|
| ❌ містити SQL або ORM-запити | ✅ валідувати через InputSerializer |
| ❌ містити бізнес-логіку | ✅ викликати service (мутації) |
| ❌ оркеструвати транзакції | ✅ викликати selector (читання) |
| ❌ рахувати або трансформувати дані | ✅ форматувати через OutputSerializer |
| ❌ знати про Celery tasks напряму | ✅ повертати Response |
Помилка у стандартній архітектурі:
views.pyописується як місце для "бізнес-логіки". У clean/production архітектурі view — лише HTTP-шар. Вся логіка — уservices.pyіselectors.py.
apps.py¶
| Аспект | Опис |
|---|---|
| Призначення | Конфігурація конкретного додатку |
| Внутрішня роль | Підклас AppConfig — ініціалізація при старті Django (наприклад, підключення Signals) |
| У lifecycle запиту | Жодної участі — виконується лише один раз при старті фреймворку |
Помилка: Писати тут бізнес-логіку або запити до БД — призведе до збою при старті.
Рівень даних (Data Layer)¶
models.py¶
| Аспект | Опис |
|---|---|
| Призначення | Визначення схеми бази даних |
| Внутрішня роль | Python-класи (нащадки models.Model) → SQL-таблиці |
| Єдине джерело істини | Структура даних описана тут — не в SQL-скриптах |
| У lifecycle запиту | Використовується views.py для читання/запису |
Ментальна модель: Архітектурні креслення бази даних.
Помилка: Додати поле і думати, що БД оновиться сама. Потрібно
makemigrations+migrate.
migrations/¶
| Аспект | Опис |
|---|---|
| Призначення | Контроль версій схеми бази даних |
| Внутрішня роль | Автоматично згенеровані Python-скрипти змін схеми |
| У lifecycle запиту | Жодної участі |
| У CI/CD | Фундамент безпечного деплою — гарантує ідентичну схему на всіх серверах |
Ментальна модель: Git-коміти для таблиць БД — кожна зміна схеми зафіксована у версії.
Небезпечна помилка: Видаляти файли міграцій "для чистоти" після того, як вони застосовані — руйнує граф залежностей та синхронізацію з БД.
admin.py¶
| Аспект | Опис |
|---|---|
| Призначення | Конфігурація вбудованого адмін-інтерфейсу |
| Внутрішня роль | Реєструє моделі на admin.site → CRUD через /admin/ |
| У lifecycle запиту | Обробляє лише запити на шляхи /admin/ |
| Економить | Сотні годин розробки внутрішніх дашбордів |
Ментальна модель: Бек-офіс магазину — тільки для довіреного персоналу.
Помилка: Використовувати адмін-панель як фронтенд для звичайних клієнтів.
Рівень представлення (Presentation Layer)¶
templates/¶
| Аспект | Опис |
|---|---|
| Призначення | HTML-структура з динамічними даними |
| Внутрішня роль | DTL (Django Template Language) — {{ змінні }}, {% теги %} |
| У lifecycle запиту | Викликається наприкінці View для генерації HTML-рядка |
| У сучасних SPA | Замінюється JSON-відповіддю якщо фронтенд — React/Vue |
Ментальна модель: Бланк документа з порожніми полями для заповнення.
Помилка: Писати повноцінний Python-код в шаблонах. DTL навмисно обмежений — бізнес-логіка належить у
views.py.
static/¶
| Аспект | Опис |
|---|---|
| Призначення | Незмінні ресурси (CSS, JS, шрифти, зображення) |
| Внутрішня роль | Передається клієнту "як є", без обробки Python |
| У lifecycle запиту | У продакшені повністю оминає Django — Nginx роздає напряму |
collectstatic |
Збирає всі static-файли з додатків в STATIC_ROOT |
Ментальна модель: Фарба, меблі та декорації будівлі.
Помилка: Очікувати, що Django автоматично роздаватиме статику на реальному сервері без Nginx.
Схема взаємодії файлів при запиті¶
sequenceDiagram
participant Nginx
participant Gateway as wsgi.py
participant Route as urls.py
participant View as views.py
participant DB as models.py (ORM)
participant HTML as templates/
Nginx->>Gateway: Вхідний HTTP-запит
Gateway->>Route: Передача диспетчеру (Router)
Route->>View: Виклик відповідної функції View
View->>DB: Book.objects.all()
DB-->>View: Повертає QuerySet (Дані)
View->>HTML: render(request, 'books.html', data)
HTML-->>View: Повертає скомпільований рядок HTML
View-->>Gateway: HttpResponse(HTML)
Gateway-->>Nginx: Надсилає байти в браузер
Сучасна production-архітектура Django-проєкту¶
Стандартна структура my_project/my_app/ підходить для навчання і невеликих додатків. У середніх і великих системах застосовується інший підхід — bounded context структура.
Структура production-проєкту¶
project/
│
├── config/ ← Конфігурація (замість my_project/)
│ ├── settings/ ← Розділені settings: base.py, local.py, prod.py
│ ├── urls.py
│ ├── asgi.py
│ └── wsgi.py
│
├── apps/ ← Всі бізнес-домени
│ │
│ ├── users/ ← Один bounded context = один app
│ │ ├── models.py
│ │ ├── views.py
│ │ ├── urls.py
│ │ ├── services.py ← Бізнес-логіка (мутації)
│ │ ├── selectors.py ← Читання даних (SELECT)
│ │ ├── serializers.py ← HTTP validation + serialization
│ │ ├── permissions.py ← DRF permissions
│ │ ├── tasks.py ← Celery tasks (transport, не логіка)
│ │ ├── validators.py ← Складна доменна валідація
│ │ ├── filters.py ← django-filter
│ │ ├── admin.py
│ │ ├── tests/
│ │ └── migrations/
│ │
│ ├── flood/ ← Окремий бізнес-домен
│ ├── dem/
│ ├── ml/
│ └── papers/
│
├── shared/ ← Спільні абстракції між app-ами
│ ├── exceptions/ ← Кастомний DRF exception handler
│ ├── middleware/
│ ├── auth/
│ ├── utils/
│ └── logging/
│
├── infrastructure/ ← Інтеграції з зовнішнім світом
│ ├── redis/
│ ├── s3/
│ ├── email/
│ ├── celery/
│ └── external_apis/
│
├── requirements/
├── docker/
├── scripts/
└── manage.py
Головне правило: app = bounded context¶
App — це не технічний модуль, а окремий бізнес-домен.
✅ Правильно ❌ Неправильно
apps/users/ models/ ← глобально для всіх
apps/payments/ views/ ← глобально для всіх
apps/flood/ utils/ ← глобально для всіх
apps/papers/
Кожен app інкапсулює весь стек для свого домену: моделі, логіку, запити, серіалайзери, задачі.
Нові файли всередині app¶
Порівняно зі стандартним my_app/ у production-архітектурі з'являються нові файли:
| Файл | Роль | Детальна документація |
|---|---|---|
services.py |
Бізнес-логіка: мутації, транзакції, side effects | DJANGO_SERVICES.md |
selectors.py |
Читання даних: named queries, ORM-оптимізації | DJANGO_SELECTORS.md |
serializers.py |
HTTP ↔ domain: InputSerializer, OutputSerializer | DJANGO_SERIALIZERS.md |
tasks.py |
Celery tasks — транспортний шар, делегують у services | — |
validators.py |
Складна доменна валідація (validate_dem_resolution()) |
— |
permissions.py |
DRF permissions (IsOwner, IsAdminOrReadOnly) |
— |
filters.py |
django-filter FilterSet для динамічної фільтрації |
— |
services.py¶
# ПОГАНО — бізнес-логіка у моделі
class FloodEvent(models.Model):
def run_ml_pipeline(self):
...
# ДОБРЕ — бізнес-логіка у service
# services.py
def flood_event_process(*, event: FloodEvent) -> FloodEvent:
...
Детальніше: DJANGO_SERVICES.md
selectors.py¶
# ПОГАНО — ORM прямо у View, дублюється по всьому проєкту
FloodEvent.objects.filter(is_active=True, region_id=region_id)
# ДОБРЕ — named query у selector
# selectors.py
def flood_events_get_for_region(*, region_id: int) -> QuerySet:
return FloodEvent.objects.filter(is_active=True, region_id=region_id)
Детальніше: DJANGO_SELECTORS.md
tasks.py¶
# ПОГАНО — бізнес-логіка в Celery task
@shared_task
def calculate_everything(event_id):
event = FloodEvent.objects.get(id=event_id)
event.status = 'done'
event.save()
send_email(event.user.email, ...)
# ДОБРЕ — task тільки транспортує виклик у service
@shared_task
def process_flood_task(event_id: int):
event = flood_event_get_by_id(event_id=event_id)
flood_event_process(event=event)
views.py у production-архітектурі¶
View повинна бути максимально тонкою:
# View НЕ повинна: View ПОВИННА:
# ❌ містити SQL ✅ валідувати через serializer
# ❌ містити ML ✅ викликати service
# ❌ оркеструвати логіку ✅ повертати Response
# ❌ рахувати метрики
class FloodEventCreateAPIView(APIView):
def post(self, request):
serializer = FloodEventInputSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
event = flood_event_create(**serializer.validated_data, created_by=request.user)
return Response(FloodEventOutputSerializer(event).data, status=201)
shared/ — Спільні абстракції¶
shared/ містить код, що використовується кількома app-ами, але не належить жодному конкретному домену.
| Папка | Що всередині |
|---|---|
shared/exceptions/ |
Кастомний DRF exception handler (маппінг DjangoValidationError → HTTP 400) |
shared/middleware/ |
Middleware для логування, correlation ID, rate limiting |
shared/auth/ |
Спільна логіка аутентифікації між app-ами |
shared/utils/ |
Дрібні утиліти без бізнес-домену |
shared/logging/ |
Конфігурація структурованого логування |
# shared/exceptions/handlers.py — реєструється один раз у settings.py
# і автоматично перехоплює помилки в усіх app-ах
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'shared.exceptions.handlers.custom_exception_handler',
}
infrastructure/ — Інтеграції з зовнішнім світом¶
infrastructure/ містить адаптери до зовнішніх сервісів. Ключова ідея:
| Папка | Що всередині |
|---|---|
infrastructure/redis/ |
Redis клієнт, конфігурація кешу |
infrastructure/s3/ |
AWS S3 / MinIO клієнт для збереження файлів |
infrastructure/email/ |
Email backend (SendGrid, SES) |
infrastructure/celery/ |
Конфігурація Celery, routing задач |
infrastructure/external_apis/ |
Клієнти зовнішніх API (SlideRule NASA, OpenWeather, тощо) |
# infrastructure/s3/client.py
# S3 — це не flood domain і не dem domain.
# Це зовнішній сервіс зберігання, ізольований в infrastructure/.
class S3Client:
def upload_file(self, file_path: str, bucket: str) -> str:
...
App-и імпортують з infrastructure, але infrastructure не знає про app-и.
Порівняння: навчальна vs production структура¶
| Аспект | Навчальна (стандартна) | Production |
|---|---|---|
| Конфігурація | my_project/settings.py |
config/settings/base.py + local.py + prod.py |
| App-и | my_app/ у корені |
apps/users/, apps/flood/ |
| Бізнес-логіка | У views.py |
У services.py |
| ORM-запити | У views.py або models.py |
У selectors.py |
| Серіалізація | ModelSerializer |
InputSerializer + OutputSerializer |
| Зовнішні сервіси | Напряму у views.py |
infrastructure/ |
| Спільний код | utils.py у корені |
shared/ |
Навчальна структура — правильна для вивчення Django. Production-структура — правильна коли проєкт росте і над ним працює команда.