Настройка Django ORM для Python веб-застосунку
Django ORM вбудована у фреймворк і не потребує окремої встановлення, але правильна конфігурація суттєво впливає на продуктивність та масштабованість. Розглядаємо Django 4.2+ з PostgreSQL.
Підключення до бази даних
У settings.py визначаємо кілька баз при необхідності:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': env('DB_NAME'),
'USER': env('DB_USER'),
'PASSWORD': env('DB_PASSWORD'),
'HOST': env('DB_HOST', default='127.0.0.1'),
'PORT': env('DB_PORT', default='5432'),
'CONN_MAX_AGE': 60,
'OPTIONS': {
'connect_timeout': 10,
'options': '-c search_path=public',
},
},
}
CONN_MAX_AGE включає persistent connections — Django не буде закривати з'єднання з БД після кожного HTTP-запиту, що зменшує latency. Стандартна практика для gunicorn з кількома воркерами.
Моделі
# catalog/models.py
from django.db import models
class Product(models.Model):
title = models.CharField(max_length=500)
slug = models.SlugField(unique=True, max_length=520)
category = models.ForeignKey(
'Category',
on_delete=models.PROTECT,
related_name='products',
)
price = models.DecimalField(max_digits=12, decimal_places=2)
status = models.CharField(
max_length=10,
choices=[('draft', 'Draft'), ('published', 'Published')],
default='draft',
)
tags = models.ManyToManyField('Tag', blank=True, related_name='products')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
indexes = [
models.Index(fields=['status', '-created_at']),
models.Index(fields=['category', 'status']),
]
Кастомні менеджери та QuerySet
class PublishedProductQuerySet(models.QuerySet):
def published(self):
return self.filter(status='published')
def with_category(self):
return self.select_related('category')
def with_tags(self):
return self.prefetch_related('tags')
class ProductManager(models.Manager):
def get_queryset(self):
return PublishedProductQuerySet(self.model, using=self._db)
def published(self):
return self.get_queryset().published()
# У моделі:
# objects = ProductManager()
Анотації та агрегації
from django.db.models import Count, Avg, F, Q
stats = (
Category.objects
.annotate(
product_count=Count('products', filter=Q(products__status='published')),
avg_price=Avg('products__price', filter=Q(products__status='published')),
)
.filter(product_count__gt=0)
.order_by('-product_count')
)
Міграції на production
Кілька правил, які гарантують відсутність простоїв:
- Додавання nullable-колонки не блокує таблицю в PostgreSQL 11+
- Додавання індексу — тільки
CONCURRENTLY: AddIndex використовує його автоматично для PostgreSQL - Переймену вання колонки — завжди у два етапи: добавити нову → скопіювати дані → видалити старку
-
--fakeміграція потрібна тільки для синхронізації стану без переповторного застосування SQL
python manage.py migrate --plan
python manage.py migrate
python manage.py showmigrations
Терміни
Первісна настройка нового Django-проекту з моделями, міграціями, роутером на реплику, базовими менеджерами: 1 день. Рефакторинг існуючого проекту для усунення N+1 запитів, додавання індексів: 1–2 дні.







