Перейти к основному содержимому

Django в Python

Разработчику Архитектору

Django в Python

См. также: Первая программа на Django · Справочник · Практикум — доска объявлений · FastAPI · Flask · Базы данных в Python


Что такое Django?

Django - это готовый набор инструментов, чтобы сделать сайт, где есть:

  • база данных;
  • вход/регистрация;
  • админка (где можно добавлять/редактировать записи);
  • страницы HTML;
  • API по необходимости.

Фреймворк сам решит, как всё организовать, а пользователю лишь надо наполнить всё смыслом.

Нужен он, чтобы не писать с нуля то, что уже есть во всех сайтах - формы, логин/пароль, связь с базой и безопасность (чтоб не взломали через SQL или XSS). Так что, если нужно быстро сделать сайт на Python, то Django в помощь.

Django работает так:

  • берёт наш запрос (например, mysite.com/blog/);
  • смотрит в свой список маршрутов;
  • вызывает нужную функцию (view);
  • эта функцию лезет в базу данных через ORM (удобные Python-конструкции вместо прямого SQL);
  • возвращает HTML-страницу или JSON.

Так что происходит связка между URL, кодом, БД и страницей.

Устанавливается он просто в несколько шагов:

Код ITЗагрузка примера кода…

Разбор — команды последовательно создают каркас проекта, отдельное приложение и базовые таблицы, после чего runserver поднимает локальный HTTP-сервер.

Теперь можно открыть http://127.0.0.1:8000 и увидеть заглушку. Так создаётся структура для написания сайта.

Django используется в крупных проектах (Instagram, Pinterest, YouTube раньше, Disqus), сайтах госуслуг и банков, корпоративных порталах и API-сервисах.

Он не подходит для:

  • микросервисов;
  • высоконагруженных чатов в реальном времени;
  • чего-то минималистичного.

Для вышеперечисленного лучше что-то проще - Flask или FastAPI.

Так что ключевое отличие Django в том, что он попросту тяжелый.

Django — это свободный, высокий, серверный (бэкенд) веб-фреймворк, написанный на языке программирования Python и распространяемый под лицензией BSD. Его ключевой принцип — "разработка, ориентированная на повторное использование и „не повторяй себя“ (DRY)". Django не является микросервисной платформой или узкоспециализированным решением — он представляет собой монолитный, полный стек, предназначенный для быстрого создания веб-приложений со сложной логикой, строгой структурой и высокой степенью интеграции между компонентами. Именно это делает его особенно ценным в образовательных целях, в государственных и корпоративных проектах, где критичны надёжность, документируемость и предсказуемость поведения системы.

Несмотря на то, что в англоязычной литературе Django часто называют фреймворком по шаблону MTV (Model–Template–View), технически он реализует классическую архитектуру MVC (Model–View–Controller), в которой компонент View отвечает за логику представления данных пользователю, а Controller частично реализуется механизмами маршрутизации и обработчиков запросов внутри фреймворка. В контексте Django View играет роль контроллера, а Template — представления. Терминологическое расхождение носит исторический характер и не влияет на архитектурные свойства фреймворка.


История создания и эволюция версий

Django появился в 2003 году в газете The Lawrence Journal-World (штат Канзас, США), где разработчики Эдриан Холловей (Adrian Holovaty) и Саймон Уиллисон (Simon Willison) создали внутренний инструментарий для ускорения производства новостных веб-проектов. Главной задачей было сократить время от идеи до запуска — отсюда и девиз фреймворка: "The web framework for perfectionists with deadlines".

Первая публичная версия (0.90) была выпущена в июле 2005 года. С тех пор развитие фреймворка стало общественным: к 2008 году — 1.0 (знаменательная версия, ввёдшая чёткий принцип обратной совместимости); к 2013 — 1.6 (поддержка транзакций, улучшенная admin-панель); 1.8 (LTS — длительная поддержка); 2.0 (требование Python 3.5+, упрощение URL-механизмов); 3.0 (асинхронные представления, ASGI); 3.2 (LTS); 4.0 (удаление устаревших API); 4.2 (LTS, до апреля 2026 года); и, наконец, 5.0 — выпущенная в декабре 2023 года, с усилением асинхронных возможностей, обновлённым шаблонизатором, расширением поддержки PostgreSQL-специфичных типов и улучшенной системой миграций.

Каждая LTS-версия (Long-Term Support) получает исправления безопасности и критические фиксы в течение трёх лет, что делает их предпочтительными для промышленного развёртывания.


Архитектура Django

Django строится как многоуровневая система:

  1. Ядро фреймворка — набор базовых модулей, отвечающих за инициализацию, маршрутизацию, обработку исключений, настройки и управление приложениями.
  2. Приложения (apps) — логические модули проекта, изолированные по функциональности. Например, users, blog, payments. Каждое приложение — это самостоятельный Python-пакет со своей структурой.
  3. Слой представления (views) — обработчики HTTP-запросов. Могут быть реализованы как функции (function-based views) или как классы (class-based views, CBV), что позволяет использовать наследование, композицию и полиморфизм.
  4. Шаблонизатор (templates) — система преобразования данных в HTML (или иной текстовый формат). Поддерживает наследование шаблонов, включение, теги, фильтры, автоматическое экранирование.
  5. Модельный слой (models) — описание структуры данных, отображаемой в реляционную СУБД через ORM. Модели определяют поля и поведение (методы экземпляра, менеджеры, пользовательские QuerySet’ы).
  6. Система маршрутизации (URL dispatcher) — механизм сопоставления входящих URL с обработчиками (views) посредством регулярных выражений или path-конвертеров.
  7. Мидлвары (middleware) — цепочка промежуточных обработчиков, перехватывающих запрос до попадания в view и ответ перед отправкой клиенту. Позволяют реализовать аутентификацию, CORS, логирование, кэширование и другие кросс-функциональные задачи.
  8. Система сигналов (signals) — механизм слабосвязанного взаимодействия между компонентами. Аналог событийной модели, но без встроенной гарантии доставки.

Все слои работают в едином контексте Django — инициализация при старте WSGI/ASGI или при вызове manage.py:

export DJANGO_SETTINGS_MODULE=mysite.settings
python manage.py check
python manage.py runserver

Структура Django-проекта

При создании проекта командой django-admin startproject mysite генерируется следующая древовидная структура:

mysite/
├── manage.py
├── mysite/
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ ├── asgi.py
│ ├── wsgi.py
│ └── ...
└── ...

Разберём каждый элемент:

  • manage.py — точка входа для административных задач (устанавливает DJANGO_SETTINGS_MODULE, вызывает execute_from_command_line):
python manage.py startapp blog
python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser
python manage.py test
  • __init__.py — пустой файл, обозначающий, что каталог mysite/ является Python-пакетом. Без него импорты не работают.

  • settings.py — центральный конфигурационный файл. Содержит параметры:

    • DEBUG (режим отладки — ни при каких условиях не включать в продакшене),
    • ALLOWED_HOSTS (список доменов, на которых разрешено развёртывание),
    • INSTALLED_APPS (список активных приложений, включая встроенные: django.contrib.admin, django.contrib.auth и т.п.),
    • MIDDLEWARE (цепочка мидлвар),
    • ROOT_URLCONF (путь к главному файлу маршрутов),
    • DATABASES (конфигурация СУБД — по умолчанию SQLite),
    • TEMPLATES, STATIC_URL, STATICFILES_DIRS, MEDIA_URL, SECRET_KEY и многие другие.
  • urls.py — корневой маршрутизатор:

from django.urls import path, include

urlpatterns = [
path("admin/", admin.site.urls),
path("blog/", include("blog.urls")),
]

Разбор: path() связывает URL и обработчик, а include("blog.urls") передаёт подмаршруты приложению blog.

  • wsgi.py — точка входа для WSGI-совместимых серверов (Gunicorn, uWSGI). Используется в традиционных синхронных развёртываниях.

  • asgi.py — аналог для ASGI (Asynchronous Server Gateway Interface), необходим для обработки веб-сокетов, long polling и асинхронных view. Основан на стандарте ASGI 3.0.

После создания приложения (python manage.py startapp blog) в нём появляется:

python manage.py startapp blog
  • apps.py — класс конфигурации приложения (BlogConfig), позволяющий задавать метаданные (имя, метку, готовность к запуску через ready()).

  • models.py — место определения моделей данных. Каждая модель — это класс, наследуемый от django.db.models.Model. Поля объявляются как атрибуты класса.

  • views.py — обработчики запросов. В простейшем случае — функция, принимающая request и возвращающая HttpResponse. В сложных — классы, наследующие View, TemplateView, ListView и прочие.

  • admin.py — регистрация моделей в интерфейсе администратора. Позволяет автоматически генерировать CRUD-интерфейс без написания HTML.

  • tests.py — тесты (рекомендуется выносить в отдельный каталог tests/, но по умолчанию — здесь).

  • migrations/ — каталог, в который makemigrations помещает Python-файлы, описывающие изменения схемы БД. Каждая миграция — это класс, наследуемый от Migration, содержащий operations (список операций вроде CreateModel, AddField) и dependencies.

  • static/ и templates/ — папки для статики (CSS, JS, изображения) и шаблонов соответственно. Django находит их по путям, указанным в STATICFILES_DIRS и DIRS внутри TEMPLATES.

Шаблоны организуются по принципу наследования. Например, base.html определяет общую структуру с {% block content %}{% endblock %}, а blog/post_list.html наследует его и переопределяет блок.


Компоненты Django

Django называют "фреймворком с батарейками", поскольку он включает в себя каркас для маршрутизации, представлений, и полный набор инструментов, достаточных для построения полноценного веб-приложения "из коробки". Эти компоненты органично вшиты в архитектуру, имеют унифицированный API и могут быть отключены или заменены, но их наличие обеспечивает согласованность разработки и снижает порог входа.


ORM (Object-Relational Mapper)

Play ITЗагрузка интерактивного демо…

Ядро модельного слоя. Позволяет описывать сущности предметной области на языке Python, не касаясь SQL напрямую. Преобразует операции над объектами (BlogPost.objects.filter(published=True)) в оптимизированные SQL-запросы, адаптированные под конкретную СУБД (PostgreSQL, MySQL, SQLite, Oracle). ORM поддерживает транзакции (atomic), агрегации (Count, Sum, Avg), аннотации, подзапросы, сырой SQL при необходимости (extra, raw), и гарантирует защиту от SQL-инъекций за счёт параметризованных запросов.


Шаблонизатор

System, построенная на принципах наследования, композиции и декларативного вывода. Шаблоны — это текстовые файлы с синтаксисом тегов ({% ... %}) и переменных ({{ ... }}).

  • {% extends "base.html" %} — наследование.
  • {% block name %}...{% endblock %} — переопределяемые участки.
  • {% include "partial.html" %} — вставка.
  • {% if user.is_authenticated %}...{% endif %} — условная логика (ограниченная — бизнес-логика не должна быть в шаблоне).
  • {{ value|date:"d.m.Y" }} — фильтры (преобразования данных, применяемые в момент рендеринга).

Шаблонизатор автоматически экранирует переменные (<script>&lt;script&gt;), предотвращая XSS. Экранирование можно отключить (&#123;&#123; html|safe &#125;&#125;), но только при полной уверенности в источнике.


Встроенная админка (django.contrib.admin)

Не просто интерфейс для CRUD — это полностью настраиваемое приложение, построенное на тех же моделях, формах и представлениях, что и пользовательский код. Админка генерирует формы на основе метаданных моделей, учитывает права доступа (User, Group, Permission), поддерживает поиск, фильтрацию, массовые действия. При этом её можно расширять — переопределять шаблоны, добавлять кастомные action’ы, интегрировать сторонние виджеты (например, редакторы Markdown).


Система аутентификации и авторизации (django.contrib.auth)

Реализует стандартные механизмы:

  • Регистрация/вход/выход/сброс пароля.
  • Модели User, Group, Permission.
  • Декораторы (@login_required, @permission_required) и миксины (LoginRequiredMixin).
  • Бэкенды аутентификации (по умолчанию — по логину/паролю, но можно подключить LDAP, OAuth2 через сторонние пакеты).
  • Сессии (django.contrib.sessions), хранящие состояние между запросами (в БД, кэше, файлах).

Система не навязывает единый подход к разграничению доступа: можно использовать проверки на уровне представлений, шаблонов (&#123;% if perms.blog.add_post %&#125;), или даже в ORM (get_queryset() в админке).


REST API и django-rest-framework

Сам Django не включает полноценную поддержку REST — это делает сторонний, но де-факто стандартный пакет Django REST Framework (DRF). Однако ядро предоставляет всё необходимое для его работы — сериализаторы могут опираться на модели, маршрутизация — на URL-конфигурацию, аутентификация — на contrib.auth. DRF добавляет:

  • Сериализаторы (ModelSerializer, HyperlinkedModelSerializer).
  • Классы представлений (APIView, GenericAPIView, ViewSet).
  • Роутеры (SimpleRouter, DefaultRouter).
  • Пагинацию, фильтрацию, документацию (Swagger/OpenAPI).

Без DRF можно построить API вручную — через JsonResponse и @csrf_exempt, но это нарушает принцип DRY и не масштабируется.


Система кэширования (django.core.cache)

Интерфейс абстрагирует конкретные бэкенды:

  • В памяти (LocMemCache) — для разработки.
  • В Redis/Memcached — для продакшена.
  • В файловой системе (редко).

Кэширование может применяться:

  • На уровне всего сайта (CacheMiddleware).
  • На уровне view (@cache_page).
  • На уровне шаблона (&#123;% load cache %&#125;&#123;% cache 600 sidebar %&#125;...&#123;% endcache %&#125;).
  • На уровне данных (cache.set, cache.get).

Поддерживает теги зависимости и автоматическую инвалидацию при использовании cacheops или кастомной логики.


Прочие встроенные компоненты

  • Формы (django.forms) — валидация, приведение типов, рендеринг HTML, защита от CSRF. Умеют работать с моделями (ModelForm).
  • Мидлвары — стандартные — AuthenticationMiddleware, SessionMiddleware, CsrfViewMiddleware, SecurityMiddleware. Позволяют внедрять логику "поперёк" запроса.
  • Интернационализация (i18n) — маркировка строк (gettext, _), переключение языка, форматирование дат/чисел под локаль.
  • Система сигналовpre_save, post_save, m2m_changed. Используется для триггерной логики, но не заменяет прямые вызовы методов.
  • Тестовый клиент (django.test.Client) — имитация HTTP-запросов внутри тестов без запуска сервера.

Эти компоненты не изолированы — админка использует формы, формы опираются на валидацию, валидация может задействовать сигналы, сигналы — писать в кэш. Такая композиция обеспечивает целостность системы.


Маршрутизация

Маршрутизация в Django — это иерархическая система сопоставления, где корневой urls.py делегирует подпути приложениям.


Маршрутизатор (URL dispatcher)

Определяется как список urlpatterns, содержащий объекты path() или re_path().

  • path('blog/<int:year>/', views.year_archive) — использует встроенные конвертеры (int, str, slug, uuid, path).
  • re_path(r'^blog/(?P<year>[0-9]{4})/$', views.year_archive) — для сложных паттернов.

Каждый элемент маршрута может:

  • Напрямую указывать на функцию/класс (views.PostList).
  • Делегировать поддерево через include('blog.urls').
  • Иметь имя (name='post_detail'), чтобы генерировать URL в коде или шаблонах (&#123;% url 'post_detail' pk=5 %&#125;).

При запросе GET /blog/2024/ Django:

  1. Ищет совпадение в ROOT_URLCONF.
  2. Находит path('blog/', include('blog.urls')).
  3. Передаёт остаток пути (2024/) в blog/urls.py.
  4. Там сопоставляет с path('<int:year>/', views.archive).
  5. Вызывает views.archive(request, year=2024).

Это — диспетчеризация, а не простое регулярное выражение: порядок маршрутов важен (первое совпадение выигрывает), и система не допускает неоднозначности без явного указания.


Классовые представления (CBV)

Функциональные view (def post_list(request)) подходят для простых страниц. Class-Based Views инкапсулируют повторяющийся сценарий — список (ListView), одна запись (DetailView), создание и правка (CreateView, UpdateView), форма без модели (FormView).

from django.views.generic import ListView
from .models import Post

class PostListView(ListView):
model = Post
template_name = 'blog/list.html'
context_object_name = 'posts'
paginate_by = 10

В urls.py: path('', PostListView.as_view(), name='post_list'). Пагинация, контекст шаблона и выборка queryset настраиваются атрибутами класса или переопределением методов (get_queryset, get_context_data).

Таблица примесей, formsets, встроенные view входа и сброса пароля — в Справочнике по Django.


Жизненный цикл разработки

Процесс создания приложения в Django строго регламентирован — это гарантия воспроизводимости.

  1. Создание проекта
django-admin startproject mysite
cd mysite
  1. Создание приложения
python manage.py startapp blog

Затем добавляем 'blog' в INSTALLED_APPS.

  1. Настройка базы данных
    В settings.py:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'mydb',
'USER': 'myuser',
'PASSWORD': 'mypass',
'HOST': 'localhost',
'PORT': '5432',
}
}

(Для разработки допустим sqlite3, но не в продакшене.)

  1. Определение моделей
    В blog/models.py:
from django.db import models

class Author(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField()

class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
published = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey(Author, on_delete=models.CASCADE)

Разбор — ForeignKey задаёт связь "многие к одному", CASCADE удаляет связанные Post вместе с автором, а auto_now_add фиксирует время создания.

  1. Генерация и применение миграций
python manage.py makemigrations
python manage.py migrate

Команда makemigrations анализирует изменения в моделях и создаёт Python-файл в blog/migrations/0001_initial.py. migrate применяет его к БД.

  1. Создание представлений
    В blog/views.py:
from django.shortcuts import render
from .models import Post

def post_list(request):
posts = Post.objects.filter(published=True).order_by('-created_at')
return render(request, 'blog/post_list.html', {'posts': posts})

Разбор: QuerySet фильтрует опубликованные записи, сортирует по дате и передаёт результат в шаблон через render.

  1. Настройка маршрутов
    blog/urls.py:
from django.urls import path
from . import views

urlpatterns = [
path('', views.post_list, name='post_list'),
]

mysite/urls.py:

from django.urls import include, path

urlpatterns = [
path('blog/', include('blog.urls')),
path('admin/', admin.site.urls),
]
  1. Шаблоны
    blog/templates/blog/post_list.html:
{% extends "base.html" %}

{% block content %}
<h1>Публикации</h1>
<ul>
{% for post in posts %}
<li>{{ post.title }} ({{ post.created_at|date:"d.m.Y" }})</li>
{% empty %}
<li>Нет опубликованных записей.</li>
{% endfor %}
</ul>
{% endblock %}
  1. Запуск сервера разработки
python manage.py runserver

Сервер runserverне для продакшена. Он однопоточный, не обрабатывает статику в DEBUG=False, и не масштабируется. Для развёртывания используют Gunicorn + Nginx или Daphne (для ASGI).


ORM

Типы полей модели

Каждое поле — экземпляр класса Field, определяющий:

  • Как данные хранятся в БД.
  • Как они валидируются.
  • Как они конвертируются в Python-объект.

Ключевые типы:

  • CharField(max_length=255) — строка фиксированной длины (VARCHAR в SQL). Обязателен max_length.
  • TextField() — длинный текст (TEXT). Без ограничения длины.
  • EmailField() — валидация по формату email.
  • GenericIPAddressField(protocol='both') — IPv4/IPv6.
  • SlugField() — строка, пригодная для URL (латиница, цифры, дефисы). Часто используется вместе с prepopulated_fields в админке.
  • URLField() — валидация URL.
  • UUIDField() — уникальный идентификатор (используется как первичный ключ вместо AutoField в распределённых системах).
  • AutoField() / BigAutoField() — автоинкрементный целочисленный PK (по умолчанию для id).
  • IntegerField(), BigIntegerField(), SmallIntegerField() — целые числа разного диапазона.
  • DecimalField(max_digits=10, decimal_places=2) — точная десятичная дробь (для денег).
  • FloatField() — приблизительное число с плавающей точкой (IEEE 754).
  • DateField(), TimeField(), DateTimeField() — дата, время, дата+время.
    • auto_now=True — обновляется при save().
    • auto_now_add=True — устанавливается один раз при создании.
  • DurationField() — разница во времени (timedelta в Python, INTERVAL в PostgreSQL).
  • FileField(upload_to='uploads/'), ImageField() — загрузка файлов. ImageField дополнительно проверяет MIME-тип и размеры.
  • BinaryField() — сырые байты (BLOB).
  • JSONField() — хранение структурированных данных (только в PostgreSQL, MySQL 5.7+, SQLite 3.38+).

Поля могут иметь параметры — null, blank, default, choices, help_text, verbose_name.


Методы QuerySet API

ORM возвращает QuerySet — ленивый, цепляемый объект. Выполнение SQL происходит при итерации, срезе, len(), list().

  • filter(&#42;&#42;kwargs) — выборка по условиям.
Post.objects.filter(published=True, author__name='Иван')
# WHERE published = TRUE AND author.name = 'Иван'
  • get(&#42;&#42;kwargs) — получение одного объекта. Выбрасывает DoesNotExist или MultipleObjectsReturned.
post = Post.objects.get(slug='hello-world')
  • create(&#42;&#42;kwargs) — создание и сохранение за один вызов.
Post.objects.create(title='Новая запись', content='...', author=author)
  • update(&#42;&#42;kwargs) — массовое обновление (без вызова save(), без сигналов pre_save).
Post.objects.filter(published=False).update(published=True)
  • delete() — массовое удаление.
Post.objects.filter(created_at__lt='2020-01-01').delete()
  • order_by('field'), distinct(), values(), values_list(), annotate(), aggregate() — для сортировки, дедупликации, выборки словарей/кортежей, расчёта агрегатов.

ORM поддерживает связки:

  • ForeignKeyauthor__name (двойное подчёркивание для перехода через связь).
  • ManyToManyFieldpost__tags__name.
  • select_related() — JOIN для ForeignKey (оптимизация N+1).
  • prefetch_related() — отдельный запрос для ManyToMany и обратных связей.

Миграции

Миграции — это код, управляющий структурой БД. Они решают две задачи:

  1. История изменений (кто, когда, что изменил в схеме).
  2. Воспроизводимость (новый разработчик или сервер могут достичь актуального состояния БД одной командой).

Процесс:

  1. Меняется models.py.
  2. makemigrations сравнивает текущее состояние моделей с последней миграцией и генерирует Python-файл.
  3. migrate применяет миграции в порядке зависимостей.

Миграции — необратимы по умолчанию. --fake и --fake-initial используются только в чрезвычайных ситуациях. Для отката пишут reverse_code вручную или используют migrate <app> <previous_migration>.

Сложные сценарии:

  • Данные в миграции — через RunPython.
def populate_authors(apps, schema_editor):
Author = apps.get_model('blog', 'Author')
Author.objects.create(name='Админ', email='admin@example.com')
  • Разделение миграций — когда одна модель влияет на другую, но приложения независимы.
  • Совместимость — миграции должны быть идемпотентными и не ломать работающие приложения при развёртывании "на лету".

Админка Django

Как получить доступ

  1. Создать суперпользователя:
python manage.py createsuperuser
  1. Запустить сервер:
python manage.py runserver
  1. Перейти по /admin/, войти.

Как это работает "под капотом"

Админка — это Django-приложение, зарегистрированное как django.contrib.admin.

  • admin.site — глобальный инстанс AdminSite.
  • admin.site.register(Model, ModelAdmin) — связывает модель с её конфигурацией.
  • ModelAdmin — класс, определяющий:
    • Какие поля отображать (list_display, list_filter).
    • Как искать (search_fields).
    • Как редактировать (fields, fieldsets, readonly_fields).
    • Как валидировать (clean()).
    • Как обрабатывать массовые действия (actions).

Пример настройки:

Код ITЗагрузка примера кода…

Разбор: действие make_published выполняет массовый UPDATE и показывает администратору количество обновлённых записей.

Админка использует те же шаблоны, что и пользовательские приложения (admin/base_site.html можно переопределить), те же формы (ModelForm), и те же сигналы. Это позволяет, например:

  • Добавить в админку график статистики через кастомный view и шаблон.
  • Интегрировать внешний API при сохранении объекта.
  • Ограничить видимость записей по правам пользователя (get_queryset()).
  • Редактировать связанные записи на одной странице через inlines (TabularInline, StackedInline) — удобно для комментариев, строк заказа, вложенных сущностей.

Важно: админка — не интерфейс для конечного пользователя. Она предназначена для управления контентом и конфигурацией. Для публичных интерфейсов строятся отдельные представления.

Расширенные приёмы (fieldsets, методы в list_display, отдельный AdminSite) — в Справочнике по Django.


Сессия хранит состояние между запросами (корзина, черновик, флаг "просмотрено"). Django ставит cookie sessionid, данные — в БД или Redis (SESSION_ENGINE). Объект request.session ведёт себя как словарь.

Cookie задают вручную через response.set_cookie() — для некритичных флагов; пароли и права — только через сессию и contrib.auth.

Кеш снимает нагрузку с БД и шаблонов — фрагменты в шаблоне, @cache_page на view, cache.set/get в коде. В кластере из нескольких воркеров нужен общий бэкенд (Redis). Подробные таблицы уровней кеширования — 301.md §15.


PostgreSQL как основная СУБД

Для продакшена Django ориентируют на PostgreSQLJSONField, массивы, полнотекстовый поиск (SearchVector), ExclusionConstraint, надёжные транзакции. SQLite остаётся удобным для обучения и тестов.

Смена движка — в DATABASES['default']['ENGINE'] и миграции на чистой или перенесённой базе. Специфичные возможности — 301.md, PostgreSQL.


Асинхронность в Django

Django изначально создавался как синхронный фреймворк. Поддержка асинхронности появилась постепенно, начиная с версии 3.0, и реализована как надстройка над существующей архитектурой. Это принципиально: асинхронность в Django — инструмент для конкретных задач.


Что стало возможным

  1. Асинхронные представления (async views)
async def async_view(request):
data = await fetch_external_api() # I/O-bound операция
return JsonResponse({"data": data})

Такие view обрабатываются ASGI-сервером (Daphne, Uvicorn) без блокировки потока на время ожидания. Однако:

  • ORM по-прежнему синхронный. Вызов await Post.objects.afilter(...) возможен только в асинхронном контексте и требует asyncpg (PostgreSQL) или совместимого бэкенда.
  • Синхронный код внутри async-view блокирует весь цикл событий — его следует оборачивать в sync_to_async.
  1. ASGI-совместимость
    Файл asgi.py позволяет развёртывать приложение на серверах, поддерживающих асинхронные протоколы:

    • HTTP/1.1 и HTTP/2 — обычные запросы.
    • Веб-сокеты — через channels (официальный проект Django).
  2. channels — расширение для событийной модели
    Не входит в ядро, но рекомендован разработчиками Django. Позволяет:

    • Обрабатывать веб-сокеты (AsyncWebsocketConsumer).
    • Организовывать фоновые задачи через channel layers (Redis, In-Memory).
    • Строить чаты, уведомления в реальном времени.
      Важно: channels не заменяет Celery — он для короткоживущих, связанных с соединением событий. Долгие задачи (генерация отчётов, обработка видео) — в Celery или RQ.

Ограничения и компромиссы

  • ORM остаётся частично синхронным. Полностью асинхронный доступ к данным (Post.objects.acreate(...)) доступен, но требует явного использования асинхронных методов и поддержки СУБД.
  • Мидлвары должны быть помечены как sync/async. Смешивание вызывает SynchronousOnlyOperation.
  • Производительность не растёт автоматически. Асинхронность эффективна при высокой доле I/O (внешние API, медленные БД), но ухудшает performance при CPU-bound операциях.
  • Сложность отладки возрастает. Стек вызовов раздваивается: sync/async boundary требует явного преобразования (sync_to_async, async_to_sync).

Таким образом, асинхронность в Django — целенаправленный выбор для решения конкретных проблем масштабируемости. Для большинства CRUD-приложений, особенно в госсекторе или корпоративных системах, синхронный стек остаётся предпочтительным: он проще, стабильнее и лучше документирован.


Безопасность

Django включает широкий набор защит по умолчанию, но никакой фреймворк не делает приложение безопасным автоматически.


Защита по умолчанию

  • CSRF-токены — включены через CsrfViewMiddleware. Формы в шаблонах должны содержать &#123;% csrf_token %&#125;. REST API (особенно stateless) требуют отключения или замены на другие методы (например, JWT).
  • XSS-защита — автоматическое экранирование в шаблонах. Риск возникает только при использовании |safe, mark_safe(), или ручном формировании HTML.
  • SQL-инъекции — исключены при использовании ORM и параметризованных запросов. Риск — при extra(), raw() без экранирования.
  • Clickjacking — блокируется заголовком X-Frame-Options: DENY (настраивается через XFrameOptionsMiddleware).
  • Безопасные заголовкиSecurityMiddleware добавляет:
    • HTTP Strict Transport Security (HSTS)
    • Content Security Policy (CSP)требует ручной настройки
    • Referrer-Policy, X-Content-Type-Options, X-XSS-Protection (устарел, но иногда нужен для старых браузеров).

Что требует явной настройки

  • Content Security Policy (CSP) — не включён по умолчанию. Для активации нужен django-csp или ручное управление заголовками. Без CSP даже экранирование не спасает от атак через script-src 'unsafe-inline'.
  • Rate limiting — отсутствует в ядре. Реализуется через Nginx, Redis + django-ratelimit, или в мидлваре.
  • Двухфакторная аутентификация — через сторонние пакеты (django-otp, django-two-factor-auth).
  • Логирование безопасностиSECURE_CONTENT_TYPE_NOSNIFF, SECURE_BROWSER_XSS_FILTER устарели; современный подход — CSP + report-uri.

Критические ошибки разработчиков

  1. Отключение DEBUG = True в продакшене (раскрывает стектрейсы, настройки, SQL).
  2. Использование SECRET_KEY в репозитории.
  3. Хранение паролей в открытом виде (ORM не шифрует — только хэширует через PBKDF2/HMAC-SHA256).
  4. Доверие к request.META['HTTP_X_FORWARDED_FOR'] без валидации (подмена IP).
  5. Неправильная настройка CORS — особенно при использовании DRF.

Безопасность в Django — оборонительная стратегия: фреймворк ставит барьеры по умолчанию, но окончательная ответственность лежит на разработчике.


Тестирование

Django предоставляет мощную тестовую инфраструктуру на базе unittest, расширенную собственными утилитами.


Уровни тестирования

  1. Модульные тесты (TestCase)
    • Тестируют модели, формы, вспомогательные функции.
    • Используют in-memory SQLite (быстро, изолированно).
    • Пример:
from django.test import TestCase
from blog.models import Post

class PostModelTest(TestCase):
def test_published_posts(self):
Post.objects.create(title='Черновик', published=False)
Post.objects.create(title='Публикация', published=True)
self.assertEqual(Post.objects.published().count(), 1)
  1. Интеграционные тесты (TransactionTestCase)

    • Проверяют взаимодействие компонентов: view + модель + шаблон.
    • Сохраняют транзакции между тестами (медленнее).
  2. Тесты клиентского уровня (Client, RequestFactory)

    • Client — имитация HTTP-запросов:
response = self.client.get('/blog/')
self.assertEqual(response.status_code, 200)
self.assertContains(response, 'Публикации')
  • RequestFactory — создаёт объект HttpRequest без запуска middleware (для тестирования view в изоляции).
  1. Тесты шаблонов
    • Проверка контекста, использования блоков, фильтров.
    • response.templates, response.context.

Особенности

  • База данных создаётся заново для каждого TestCase.
  • --keepdb ускоряет повторные запуски.
  • pytest-django — альтернатива стандартному runner’у, с поддержкой fixture, параметризации, асинхронных тестов.
  • Тесты админки: admin.site.urls доступен, можно проверять рендеринг форм, права.
  • Покрытие: coverage run manage.py test && coverage report.

Тестирование в Django — не опционально. Это часть жизненного цикла — миграции, админка, формы — всё ломается при несогласованности изменений. Регулярный запуск тестов — условие поддерживаемости.


Развёртывание

Этапы развёртывания

  1. Подготовка окружения

    • DEBUG = False
    • ALLOWED_HOSTS = ['example.com', 'www.example.com']
    • SECRET_KEY — из переменной окружения (os.getenv('SECRET_KEY')).
    • Настройка логирования (LOGGING в settings.py).
  2. Статические файлы

python manage.py collectstatic

Собирает все static/ в STATIC_ROOT. Обслуживаются веб-сервером (Nginx):

location /static/ {
alias /path/to/staticfiles/;
}
  1. Медиафайлы
    Аналогично, но с учётом безопасности:

    • Ограничение MIME-типов.
    • Хранение вне корня проекта.
    • Использование signed URLs (через django-storages + S3).
  2. Сервер приложения

    • WSGI: Gunicorn (предпочтительно) + Nginx.
gunicorn mysite.wsgi:application --bind 0.0.0.0:8000 --workers 3
  1. База данных

    • PostgreSQL — рекомендуемый выбор (поддержка JSONField, ARRAY, HSTORE, надёжность).
    • Резервное копирование (pg_dump), мониторинг, connection pooling (pgBouncer).
  2. Мониторинг и логирование

    • Sentry — для отслеживания исключений.
    • Prometheus + Grafana — метрики (запросы/сек, время ответа).
    • ELK-stack — агрегация логов.

Контейнеризация

Docker не обязателен, но упрощает воспроизводимость:

FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
RUN python manage.py collectstatic --noinput
CMD ["gunicorn", "mysite.wsgi:application", "--bind", "0.0.0.0:8000"]

Важно: не запускать миграции в Dockerfile — только в entrypoint.sh при старте контейнера.


Типичные антипаттерны и как их избежать

  1. "Гигантский views.py"
    — Признак:

    • тысячи строк;
    • смесь логики;
    • шаблонов;
    • API. — Решение:
      • Вынос бизнес-логики в services.py.
      • Использовать классы (ListView, DetailView).
      • Разделять на модули: views/list.py, detail.py, api.py.
  2. N+1 запросов
    — Признак: post.author.name в цикле без select_related.
    — Решение:

    • select_related() для ForeignKey.
    • prefetch_related() для ManyToMany.
    • django-debug-toolbar для выявления.
  3. Логика в шаблонах
    — Признак: &#123;% if user.is_staff or user.is_superuser or post.author == user %&#125;.
    — Решение:

    • Вынос в метод модели (post.can_edit(user)).
    • Использование TemplateView с предварительно вычисленным контекстом.
  4. Жёсткая привязка к админке
    — Признак: "Пользователи редактируют контент только через /admin/".
    — Решение:

    • Построение пользовательских форм и интерфейсов.
    • Админка — только для технических администраторов.
  5. Игнорирование миграций
    — Признак: "Просто написал SQL вручную в pgAdmin".
    — Решение:

    • Всегда использовать makemigrations.
    • Проверка миграций в CI:
python manage.py makemigrations --check --dry-run

Сравнение Django с Flask, FastAPI и Pyramid

Выбор фреймворка — инженерное решение, основанное на требованиях к проекту, а не на хайпе. Django не "лучше" или "хуже" — он другой.

КритерийDjangoFlaskFastAPIPyramid
Тип"Батарейки в комплекте", монолитныйМикрофреймворк, минимализмСовременный, API-firstГибкий, настраиваемый
АрхитектураЖёсткая структура (apps, models, views)Свободная (любая организация кода)Слабо структурирован (но рекомендуется routers, schemas)Явная декларация компонентов
ORMВстроенный (Django ORM)Нет (обычно SQLAlchemy)Нет (обычно SQLAlchemy или Tortoise)Нет (обычно SQLAlchemy)
АдминкаДа, с полной кастомизациейНет (Flask-Admin — отдельный пакет)НетНет
ВалидацияВ формах и моделяхРучная или через MarshmallowАвтоматическая (Pydantic)Через Colander или Marshmallow
АсинхронностьЧастичная (с версии 3.0)Через Quart или Flask + asyncioПолная (на базе Starlette)Частичная (через pyramid_asyncio)
Документация APIТребует DRF + drf-yasg/SwaggerТребует сторонних инструментовАвтоматическая (OpenAPI/Swagger UI)Требует ручной настройки
Скорость разработкиВысокая (MVP за часы)Средняя (нужно собирать стек)Высокая для APIСредняя (требует проектирования)
Скорость выполненияУмеренная (накладные расходы каркаса)Высокая (минимум overhead)Очень высокая (асинхронность + Pydantic)Умеренная
Типичное применениеКорпоративные системы, CMS, порталыМикросервисы, прототипы, скриптыAPI, микросервисы, ML-инференсEnterprise-приложения, legacy-интеграция

Когда выбирать Django?

  • Требуется единая точка управления (админка + пользовательский интерфейс + API).
  • Проект ориентирован на данные и бизнес-логику, а не на высоконагруженные API.
  • Важна предсказуемость и сопровождаемость (госзаказ, финансовые системы).
  • Команда включает начинающих разработчиков — структура ускоряет онбординг.

Когда НЕ выбирать Django?

  • Чистый REST/gRPC-сервис без веб-интерфейса.
  • Высокая нагрузка на чтение (десятки тысяч RPS) — тогда FastAPI + кэш.
  • Интеграция с legacy-системами через нестандартные протоколы (тогда Flask + адаптеры).
  • Требуется полная свобода выбора ORM (например, переход с SQLAlchemy на Tortoise).

Django — консервативный, проверенный временем выбор для систем, где важнее надёжность и скорость старта, чем предельная производительность.


Расширение через сигналы и middleware

Django позволяет внедрять логику "поперёк" архитектуры без изменения ядра. Это критично для масштабирования и поддержки.


Сигналы — слабосвязанное взаимодействие

Сигналы — аналог событийной модели. Основные встроенные сигналы:

  • pre_save / post_save — перед/после сохранения объекта.
  • pre_delete / post_delete — перед/после удаления.
  • m2m_changed — при изменении ManyToMany-связи.
  • request_started / request_finished — начало/окончание обработки запроса.

Пример: инвалидация кэша при обновлении поста:

from django.db.models.signals import post_save
from django.dispatch import receiver
from django.core.cache import cache
from .models import Post

@receiver(post_save, sender=Post)
def invalidate_post_cache(sender, instance, **kwargs):
cache.delete(f'post_detail_{instance.pk}')
cache.delete('post_list')

Разбор: обработчик post_save инвалидирует кэш карточки и списка, чтобы после сохранения пользователи видели актуальные данные.

Ограничения сигналов:

  • Не гарантируют порядок выполнения.
  • Не работают при массовом обновлении (QuerySet.update()).
  • Сложно тестировать и отлаживать (неявные зависимости).
  • Не заменяют прямые вызовы методов — использовать только там, где нельзя изменить вызывающий код (например, в сторонних приложениях).

Middleware

Мидлвар — класс с методами __call__ или process_request/process_response. Выполняется в порядке, указанном в MIDDLEWARE.

Пример: логирование времени обработки:


import time

from django.utils.deprecation import MiddlewareMixin

class TimingMiddleware(MiddlewareMixin):
def process_request(self, request):
request.start_time = time.time()

def process_response(self, request, response):
duration = time.time() - request.start_time
response['X-Response-Time'] = f'{duration:.3f}s'
return response

Встроенные мидлвары, которые стоит понимать:

  • SecurityMiddleware — безопасные заголовки.
  • CsrfViewMiddleware — защита от CSRF.
  • SessionMiddleware — загрузка сессии из хранилища.
  • AuthenticationMiddleware — привязка request.user.
  • MessageMiddleware — одноразовые сообщения ("Запись сохранена").

При написании кастомного middleware:

  • Избегать тяжёлых операций в process_request (замедляет все запросы).
  • Обрабатывать исключения в process_exception.
  • Для асинхронных view — наследоваться от AsyncMiddlewareMixin.

Сигналы и middleware — инструменты ответственности: их чрезмерное использование превращает проект в "лазанью" неявных зависимостей.


Интернационализация (i18n) и локализация (l10n)

Django предоставляет полную поддержку многоязычности — от маркировки строк до форматирования дат под локаль.


Этапы внедрения

  1. Маркировка строк
    В коде:
from django.utils.translation import gettext as _
title = _('Welcome to our site')

В шаблонах:

{% load i18n %}
<h1>{% trans "Welcome to our site" %}</h1>
  1. Генерация .po-файлов
django-admin makemessages -l ru

Создаёт locale/ru/LC_MESSAGES/django.po — файл перевода.

  1. Компиляция в .mo
django-admin compilemessages
  1. Переключение языка
    • Через URL (/ru/blog/, /en/blog/ с i18n_patterns).
    • Через заголовок Accept-Language.
    • Через куки/сессию (set_language redirect view).

Особенности

  • Форматирование дат/чисел — зависит от USE_L10N = True и LANGUAGE_CODE.
    Например, &#123;&#123; date|date:"SHORT_DATE_FORMAT" &#125;&#125;05.12.2025 (ru) и 12/05/2025 (en).
  • Множественные формы — через ngettext:
ngettext('%(count)d post', '%(count)d posts', count) % {'count': count}
  • Перевод моделей — требует сторонних пакетов (django-modeltranslation, django-parler).
    В ядре — только перевод строк интерфейса, не данных.

Интернационализация в Django — зрелая, промышленная система, но требует дисциплины — каждая строка, видимая пользователю, должна быть помечена. Пропуск одной строки ломает весь перевод.


Работа с legacy-кодом

Django часто используется для модернизации старых систем. Ключевые стратегии:


1. Постепенная замена (Strangler Fig Pattern)

  • Новый функционал пишется на Django.
  • Старая система остаётся, но новые URL перехватываются Django.
  • Пример:
    • Старый /old-crm/ — обслуживается PHP.
    • Новый /crm/ — на Django.
    • Постепенно /old-crm/* перенаправляются или реверс-проксируются.

2. Обёртка вокруг legacy-логики

  • Django выступает как "тонкий" фасад:
def legacy_report_view(request):
# Вызов старого скрипта через subprocess или HTTP
result = requests.post('http://legacy/report', json=request.POST)
return JsonResponse(result.json())

3. Совместное использование базы данных

  • Django ORM работает с существующей схемой:
class LegacyUser(models.Model):
login = models.CharField(max_length=50)
# ... поля без primary_key=True, если PK не `id`
class Meta:
db_table = 'users_old'
managed = False # Django не создаёт/не меняет таблицу
  • managed = False — критично: миграции не трогают эту таблицу.

4. Интеграция через API

  • Legacy-система предоставляет REST/SOAP.
  • Django использует requests, zeep (для SOAP), кэширование, retry-логику.
  • Для SOAP — django-soap или ручная обработка через xml.etree.

5. Тестирование legacy-интеграций

  • Mock внешних вызовов (unittest.mock.patch).
  • Запись/воспроизведение запросов (vcr.py).
  • Проверка идемпотентности (повторный вызов не ломает данные).

Legacy-интеграция — организационная задача: необходимо документировать границы ответственности, точки отказа и процедуры отката.


Основа по протоколу

Базовый разбор HTTP и HTTPS находится в отдельной статье — HTTP как основа веб-интеграций.


Как читать эту статью по шагам

Материал большой, поэтому его удобно проходить в три итерации:

  1. Первый проход — архитектура, жизненный цикл запроса и базовый запуск.
  2. Второй проход — ORM, миграции, безопасность и админка.
  3. Третий проход — асинхронность, развёртывание, интеграция с legacy.

Такой порядок позволяет сначала собрать общую картину, затем закрепить инженерные детали и только после этого переходить к продвинутым темам.


Практический чек-лист для команды

При запуске Django-проекта в команде помогает единый минимальный стандарт:

  • зафиксировать версию Python и зависимостей;
  • настроить .env и разделить dev/prod-конфиг;
  • включить проверку миграций в CI;
  • ввести базовый набор тестов и линтер;
  • ограничить доступ к административному интерфейсу.

Проверка миграций в CI:

python manage.py makemigrations --check --dry-run
python manage.py test

Эти две команды быстро ловят критичные проблемы перед релизом.


Связанные статьи для углубления