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 строится как многоуровневая система:
- Ядро фреймворка — набор базовых модулей, отвечающих за инициализацию, маршрутизацию, обработку исключений, настройки и управление приложениями.
- Приложения (apps) — логические модули проекта, изолированные по функциональности. Например,
users,blog,payments. Каждое приложение — это самостоятельный Python-пакет со своей структурой. - Слой представления (views) — обработчики HTTP-запросов. Могут быть реализованы как функции (
function-based views) или как классы (class-based views, CBV), что позволяет использовать наследование, композицию и полиморфизм. - Шаблонизатор (templates) — система преобразования данных в HTML (или иной текстовый формат). Поддерживает наследование шаблонов, включение, теги, фильтры, автоматическое экранирование.
- Модельный слой (models) — описание структуры данных, отображаемой в реляционную СУБД через ORM. Модели определяют поля и поведение (методы экземпляра, менеджеры, пользовательские QuerySet’ы).
- Система маршрутизации (URL dispatcher) — механизм сопоставления входящих URL с обработчиками (views) посредством регулярных выражений или path-конвертеров.
- Мидлвары (middleware) — цепочка промежуточных обработчиков, перехватывающих запрос до попадания в view и ответ перед отправкой клиенту. Позволяют реализовать аутентификацию, CORS, логирование, кэширование и другие кросс-функциональные задачи.
- Система сигналов (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> → <script>), предотвращая XSS. Экранирование можно отключить ({{ html|safe }}), но только при полной уверенности в источнике.
Встроенная админка (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), хранящие состояние между запросами (в БД, кэше, файлах).
Система не навязывает единый подход к разграничению доступа: можно использовать проверки на уровне представлений, шаблонов ({% if perms.blog.add_post %}), или даже в 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). - На уровне шаблона (
{% load cache %}{% cache 600 sidebar %}...{% endcache %}). - На уровне данных (
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 в коде или шаблонах ({% url 'post_detail' pk=5 %}).
При запросе GET /blog/2024/ Django:
- Ищет совпадение в
ROOT_URLCONF. - Находит
path('blog/', include('blog.urls')). - Передаёт остаток пути (
2024/) вblog/urls.py. - Там сопоставляет с
path('<int:year>/', views.archive). - Вызывает
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 строго регламентирован — это гарантия воспроизводимости.
- Создание проекта
django-admin startproject mysite
cd mysite
- Создание приложения
python manage.py startapp blog
Затем добавляем 'blog' в INSTALLED_APPS.
- Настройка базы данных
Вsettings.py:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'mydb',
'USER': 'myuser',
'PASSWORD': 'mypass',
'HOST': 'localhost',
'PORT': '5432',
}
}
(Для разработки допустим sqlite3, но не в продакшене.)
- Определение моделей
В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 фиксирует время создания.
- Генерация и применение миграций
python manage.py makemigrations
python manage.py migrate
Команда makemigrations анализирует изменения в моделях и создаёт Python-файл в blog/migrations/0001_initial.py. migrate применяет его к БД.
- Создание представлений
В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.
- Настройка маршрутов
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),
]
- Шаблоны
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 %}
- Запуск сервера разработки
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(**kwargs)— выборка по условиям.
Post.objects.filter(published=True, author__name='Иван')
# WHERE published = TRUE AND author.name = 'Иван'
get(**kwargs)— получение одного объекта. ВыбрасываетDoesNotExistилиMultipleObjectsReturned.
post = Post.objects.get(slug='hello-world')
create(**kwargs)— создание и сохранение за один вызов.
Post.objects.create(title='Новая запись', content='...', author=author)
update(**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 поддерживает связки:
ForeignKey→author__name(двойное подчёркивание для перехода через связь).ManyToManyField→post__tags__name.select_related()— JOIN дляForeignKey(оптимизация N+1).prefetch_related()— отдельный запрос дляManyToManyи обратных связей.
Миграции
Миграции — это код, управляющий структурой БД. Они решают две задачи:
- История изменений (кто, когда, что изменил в схеме).
- Воспроизводимость (новый разработчик или сервер могут достичь актуального состояния БД одной командой).
Процесс:
- Меняется
models.py. makemigrationsсравнивает текущее состояние моделей с последней миграцией и генерирует Python-файл.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
Как получить доступ
- Создать суперпользователя:
python manage.py createsuperuser
- Запустить сервер:
python manage.py runserver
- Перейти по
/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.
Сессии, cookie и кеш
Сессия хранит состояние между запросами (корзина, черновик, флаг "просмотрено"). 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 ориентируют на PostgreSQL — JSONField, массивы, полнотекстовый поиск (SearchVector), ExclusionConstraint, надёжные транзакции. SQLite остаётся удобным для обучения и тестов.
Смена движка — в DATABASES['default']['ENGINE'] и миграции на чистой или перенесённой базе. Специфичные возможности — 301.md, PostgreSQL.
Асинхронность в Django
Django изначально создавался как синхронный фреймворк. Поддержка асинхронности появилась постепенно, начиная с версии 3.0, и реализована как надстройка над существующей архитектурой. Это принципиально: асинхронность в Django — инструмент для конкретных задач.
Что стало возможным
- Асинхронные представления (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.
-
ASGI-совместимость
Файлasgi.pyпозволяет развёртывать приложение на серверах, поддерживающих асинхронные протоколы:- HTTP/1.1 и HTTP/2 — обычные запросы.
- Веб-сокеты — через
channels(официальный проект Django).
-
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. Формы в шаблонах должны содержать{% csrf_token %}. 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.
Критические ошибки разработчиков
- Отключение
DEBUG = Trueв продакшене (раскрывает стектрейсы, настройки, SQL). - Использование
SECRET_KEYв репозитории. - Хранение паролей в открытом виде (ORM не шифрует — только хэширует через PBKDF2/HMAC-SHA256).
- Доверие к
request.META['HTTP_X_FORWARDED_FOR']без валидации (подмена IP). - Неправильная настройка CORS — особенно при использовании DRF.
Безопасность в Django — оборонительная стратегия: фреймворк ставит барьеры по умолчанию, но окончательная ответственность лежит на разработчике.
Тестирование
Django предоставляет мощную тестовую инфраструктуру на базе unittest, расширенную собственными утилитами.
Уровни тестирования
- Модульные тесты (
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)
-
Интеграционные тесты (
TransactionTestCase)- Проверяют взаимодействие компонентов: view + модель + шаблон.
- Сохраняют транзакции между тестами (медленнее).
-
Тесты клиентского уровня (
Client,RequestFactory)Client— имитация HTTP-запросов:
response = self.client.get('/blog/')
self.assertEqual(response.status_code, 200)
self.assertContains(response, 'Публикации')
RequestFactory— создаёт объектHttpRequestбез запуска middleware (для тестирования view в изоляции).
- Тесты шаблонов
- Проверка контекста, использования блоков, фильтров.
response.templates,response.context.
Особенности
- База данных создаётся заново для каждого
TestCase. --keepdbускоряет повторные запуски.pytest-django— альтернатива стандартному runner’у, с поддержкой fixture, параметризации, асинхронных тестов.- Тесты админки:
admin.site.urlsдоступен, можно проверять рендеринг форм, права. - Покрытие:
coverage run manage.py test && coverage report.
Тестирование в Django — не опционально. Это часть жизненного цикла — миграции, админка, формы — всё ломается при несогласованности изменений. Регулярный запуск тестов — условие поддерживаемости.
Развёртывание
Этапы развёртывания
-
Подготовка окружения
DEBUG = FalseALLOWED_HOSTS = ['example.com', 'www.example.com']SECRET_KEY— из переменной окружения (os.getenv('SECRET_KEY')).- Настройка логирования (
LOGGINGвsettings.py).
-
Статические файлы
python manage.py collectstatic
Собирает все static/ в STATIC_ROOT. Обслуживаются веб-сервером (Nginx):
location /static/ {
alias /path/to/staticfiles/;
}
-
Медиафайлы
Аналогично, но с учётом безопасности:- Ограничение MIME-типов.
- Хранение вне корня проекта.
- Использование signed URLs (через
django-storages+ S3).
-
Сервер приложения
- WSGI: Gunicorn (предпочтительно) + Nginx.
gunicorn mysite.wsgi:application --bind 0.0.0.0:8000 --workers 3
- ASGI: Daphne/Uvicorn — только при использовании веб-сокетов или async views.
- Примеры
location /static/и reverse proxy — Nginx — конфиги под задачу.
-
База данных
- PostgreSQL — рекомендуемый выбор (поддержка
JSONField,ARRAY,HSTORE, надёжность). - Резервное копирование (
pg_dump), мониторинг, connection pooling (pgBouncer).
- PostgreSQL — рекомендуемый выбор (поддержка
-
Мониторинг и логирование
- 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 при старте контейнера.
Типичные антипаттерны и как их избежать
-
"Гигантский
views.py"
— Признак:- тысячи строк;
- смесь логики;
- шаблонов;
- API.
— Решение:
- Вынос бизнес-логики в
services.py. - Использовать классы (
ListView,DetailView). - Разделять на модули:
views/→list.py,detail.py,api.py.
- Вынос бизнес-логики в
-
N+1 запросов
— Признак:post.author.nameв цикле безselect_related.
— Решение:select_related()дляForeignKey.prefetch_related()дляManyToMany.django-debug-toolbarдля выявления.
-
Логика в шаблонах
— Признак:{% if user.is_staff or user.is_superuser or post.author == user %}.
— Решение:- Вынос в метод модели (
post.can_edit(user)). - Использование
TemplateViewс предварительно вычисленным контекстом.
- Вынос в метод модели (
-
Жёсткая привязка к админке
— Признак: "Пользователи редактируют контент только через/admin/".
— Решение:- Построение пользовательских форм и интерфейсов.
- Админка — только для технических администраторов.
-
Игнорирование миграций
— Признак: "Просто написал SQL вручную в pgAdmin".
— Решение:- Всегда использовать
makemigrations. - Проверка миграций в CI:
- Всегда использовать
python manage.py makemigrations --check --dry-run
Сравнение Django с Flask, FastAPI и Pyramid
Выбор фреймворка — инженерное решение, основанное на требованиях к проекту, а не на хайпе. Django не "лучше" или "хуже" — он другой.
| Критерий | Django | Flask | FastAPI | Pyramid |
|---|---|---|---|---|
| Тип | "Батарейки в комплекте", монолитный | Микрофреймворк, минимализм | Современный, 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 предоставляет полную поддержку многоязычности — от маркировки строк до форматирования дат под локаль.
Этапы внедрения
- Маркировка строк
В коде:
from django.utils.translation import gettext as _
title = _('Welcome to our site')
В шаблонах:
{% load i18n %}
<h1>{% trans "Welcome to our site" %}</h1>
- Генерация
.po-файлов
django-admin makemessages -l ru
Создаёт locale/ru/LC_MESSAGES/django.po — файл перевода.
- Компиляция в
.mo
django-admin compilemessages
- Переключение языка
- Через URL (
/ru/blog/,/en/blog/сi18n_patterns). - Через заголовок
Accept-Language. - Через куки/сессию (
set_languageredirect view).
- Через URL (
Особенности
- Форматирование дат/чисел — зависит от
USE_L10N = TrueиLANGUAGE_CODE.
Например,{{ date|date:"SHORT_DATE_FORMAT" }}→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 как основа веб-интеграций.
Как читать эту статью по шагам
Материал большой, поэтому его удобно проходить в три итерации:
- Первый проход — архитектура, жизненный цикл запроса и базовый запуск.
- Второй проход — ORM, миграции, безопасность и админка.
- Третий проход — асинхронность, развёртывание, интеграция с legacy.
Такой порядок позволяет сначала собрать общую картину, затем закрепить инженерные детали и только после этого переходить к продвинутым темам.
Практический чек-лист для команды
При запуске Django-проекта в команде помогает единый минимальный стандарт:
- зафиксировать версию Python и зависимостей;
- настроить
.envи разделить dev/prod-конфиг; - включить проверку миграций в CI;
- ввести базовый набор тестов и линтер;
- ограничить доступ к административному интерфейсу.
Проверка миграций в CI:
python manage.py makemigrations --check --dry-run
python manage.py test
Эти две команды быстро ловят критичные проблемы перед релизом.
Связанные статьи для углубления
-
Практикум — доска объявлений — сквозной проект после Первая программа на Django
-
Пошаговый старт без перегруза теорией: Первая программа на Django
-
Практика API: Первая программа на Django REST Framework
-
База по конкурентности для веб-сервисов: Асинхронность и многопоточность в Python