Согласие на обработку персональных данных в Django-проекте часто начинается с одного BooleanField. Но затем оказывается, что недостаточно помнить только факт нажатия на чекбокс: пользователь мог видеть другую редакцию документа, отозвать согласие, выбрать лишь часть категорий файлов cookie, а администратору может понадобиться журнал действий и выгрузка в CSV.

Я сделал для этого два полностью независимых Django-пакета с открытым исходным кодом: django-consent-152fz для юридически значимых согласий и django-cookies-152fz для политики файлов cookie, категорий, подключённых сервисов и окна выбора. В статье покажу реальную модель данных, минимальное подключение и то, как устроены редакции, журнал событий и выгрузка данных.


Ссылки на проекты:

Сразу обозначу границу ответственности. Это технические пакеты, а не юридическая гарантия соответствия 152-ФЗ. Они не заменяют юриста, не определяют за оператора состав персональных данных и не создают автоматически корректные документы.

Их задача — дать воспроизводимый серверный контур для документов, редакций, статусов, истории действий, пользовательского выбора файлов cookie и выгрузки данных.

Почему два пакета

Сначала согласия и файлы cookie выглядели как одна задача: пользователь что-то подтверждает, а сайт должен сохранить его выбор и историю.

Но в реальном проекте это два разных направления.

Согласие связано с целью обработки и юридическим документом. Например, пользователь оставляет заявку на курс, отправляет обращение через форму контактов или соглашается на рассылку.

Модуль файлов cookie связан с техническими категориями и подключёнными сервисами сайта: обязательными файлами cookie, аналитикой, маркетинговыми счётчиками, внешними виджетами и повторным запросом выбора после изменения политики.

Поэтому пакеты независимы:

pip install django-consent-152fz
pip install django-cookies-152fz

При необходимости можно установить оба:

pip install django-consent-152fz django-cookies-152fz

Но это не обязательная связка.

django-cookies-152fz не требует django-consent-152fz, не импортирует его модели и не использует его жизненный цикл согласий. Политики файлов cookie, категории, реестр подключённых сервисов, окно выбора, пользовательские решения, журнал событий и выгрузка данных работают внутри cookie-модуля как самостоятельный контур.

Модель согласий: цель, документ, редакция, запись, события

В коде используется не абстрактная цепочка вида Consent → ConsentVersion → UserConsent, а конкретные сущности:

ConsentPurpose       LegalDocument
       \                 /
        \               /
         DocumentRevision
                |
           ConsentRecord
                |
            ConsentEvent

ConsentPurpose — цель обработки. Например: «обработать заявку на обучение» или «ответить на обращение через форму контактов».

LegalDocument — юридический документ как постоянный контейнер.

DocumentRevision — конкретная опубликованная редакция текста. Именно к ней привязывается согласие.

ConsentRecord — запись о текущем или историческом состоянии согласия конкретного субъекта.

ConsentEvent — неизменяемый журнал переходов: выдача, отзыв, устаревание, подтверждение и другие события.

Новая редакция документа не перезаписывает старую. Если текст изменился, создаётся новый DocumentRevision. Старый ConsentRecord остаётся связанным с тем текстом, который пользователь видел в момент подтверждения.

Записи согласий в административной панели Django: цель обработки, документ, текущий статус, способ подтверждения, субъект и действие экспорта выбранных записей в CSV
Записи согласий в административной панели Django: цель обработки, документ, текущий статус, способ подтверждения, субъект и действие экспорта выбранных записей в CSV

Практически это даёт ответ на два вопроса: какой документ и какую редакцию подтвердил пользователь — и как это состояние менялось дальше.

Модуль файлов cookie — не только окно с кнопкой «Принять»

Окно выбора файлов cookie обычно воспринимают как небольшой элемент интерфейса с кнопками «Принять» и «Настроить».

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

Состав и редакция политики

CookieCategory ← записи реестра cookie и подключённых сервисов
       \                         /
        \                       /
         CookiePolicyRevision

CookieCategory — справочник категорий. Например:

  • обязательные;

  • функциональные;

  • аналитические;

  • маркетинговые.

Реестр содержит сведения о конкретных файлах cookie и подключённых сервисах: сторонних скриптах, системах аналитики или виджетах.

Для каждой записи можно хранить поставщика, назначение, категорию, срок хранения, адрес подключаемого скрипта, имена файлов cookie, стратегию очистки и признак активности.

CookiePolicyRevision фиксирует опубликованную редакцию политики вместе со снимком категорий и реестра подключённых сервисов на момент публикации.

Категории файлов cookie в административной панели Django: обязательные, функциональные, аналитические и маркетинговые.
Категории файлов cookie в административной панели Django: обязательные, функциональные, аналитические и маркетинговые.
Реестр файлов cookie и подключённых сервисов: категория, поставщик, стратегия очистки и статус активности.
Реестр файлов cookie и подключённых сервисов: категория, поставщик, стратегия очистки и статус активности.

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

Настройки отображения и выбор пользователя

Параллельно существует отдельный контур интерфейса:

CookieBannerRevision
        |
        v
CookieConsentRecord
        |
        v
CookieConsentEvent

CookieBannerRevision отвечает за текст, кнопки, вид окна выбора, настройки выбора категорий, отображение на мобильных устройствах и другие параметры интерфейса.

CookieConsentRecord хранит итоговый выбор категорий конкретного пользователя или анонимного посетителя.

CookieConsentEvent фиксирует принятие, обновление выбора, устаревание и другие действия.

Политика и окно выбора версионируются отдельно. Это позволяет изменить текст кнопки или внешний вид окна, не смешивая изменение интерфейса с изменением состава подключённых сервисов и политики.

Редакция политики файлов cookie: текст, снимок категорий и реестр подключённых сервисов на момент публикации.
Редакция политики файлов cookie: текст, снимок категорий и реестр подключённых сервисов на момент публикации.

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

Минимальная установка

Пакеты включаются добавлением приложений в INSTALLED_APPS.

Для модуля согласий:

# settings.py

INSTALLED_APPS = [
    # ...
    "django_consent_152fz",
]

Для модуля файлов cookie:

# settings.py

INSTALLED_APPS = [
    # ...
    "django_cookies_152fz",
]

Настройки enable_core и enable_cookies сохранены для обратной совместимости. Для новой интеграции главный переключатель — наличие соответствующего приложения в INSTALLED_APPS.

Маршруты модуля согласий:

# urls.py

from django.urls import include, path

urlpatterns = [
    path(
        "",
        include(
            ("django_consent_152fz.urls", "django_consent_152fz"),
            namespace="django_consent_152fz",
        ),
    ),
]

Маршруты модуля файлов cookie:

# urls.py

from django.urls import include, path

urlpatterns = [
    path(
        "cookies/",
        include(
            ("django_cookies_152fz.urls", "django_cookies_152fz"),
            namespace="django_cookies_152fz",
        ),
    ),
]

Затем применяются миграции:

python manage.py migrate

Для демонстрационного проекта или быстрого ознакомления можно загрузить примеры:

python manage.py bootstrap_152fz_sample_documents
python manage.py bootstrap_152fz_cookie_defaults

В рабочем проекте вместо примеров создаются собственные цели обработки, юридические документы, редакции и реестр файлов cookie.

Как подключить сценарий получения согласия к форме

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

Для Django-формы можно использовать готовый ConsentCaptureModeMixin:

from django import forms
from django_consent_152fz.forms import ConsentCaptureModeMixin


class ContactForm(ConsentCaptureModeMixin, forms.ModelForm):
    class Meta:
        model = ContactMessage
        fields = ("full_name", "email", "message")

Для каждого бизнес-сценария стоит заранее зафиксировать стабильные коды:

purpose_code + document_code + form_code

Например:

demo.contact
demo.course_signup
demo.certificate_request

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

from django_consent_152fz import service_api

verification_context = {
    "channel": "form",
    "form_code": "demo.contact",
}

user = request.user if request.user.is_authenticated else None

status_info = service_api.get_consent_status(
    purpose_code=purpose_code,
    document_code=document_code,
    user=user,
    anonymous_token=anonymous_token,
    verification_context=verification_context,
)

consent_required = bool(status_info.get("requires_consent"))

После успешной отправки формы и явного подтверждения пользователя создаётся запись согласия:

from django_consent_152fz import service_api
from django_consent_152fz.core.models import ConsentRecord

service_api.accept_consent(
    purpose_code=purpose_code,
    document_code=document_code,
    user=user,
    anonymous_token=anonymous_token,
    confirmation_method=ConsentRecord.ConfirmationMethod.WEB_CHECKBOX,
    verification_context=verification_context,
    audit_context=build_request_audit_context(
        request,
        source="demo.contact.form",
        anonymous_token=anonymous_token,
    ),
)

build_request_audit_context(...) здесь — вспомогательная функция проекта, которая собирает технические сведения HTTP-запроса для журнала действий.

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

Для внешних подключений предусмотрен публичный программный интерфейс:

from django_consent_152fz import service_api

Основные операции:

service_api.get_consent_status(...)
service_api.accept_consent(...)
service_api.withdraw_consent(...)
service_api.attach_anonymous_consents_to_user(...)

Окно выбора файлов cookie в шаблоне

Для модуля файлов cookie окно выбора подключается один раз в базовом шаблоне:

{% load cookies_tags %}

{# ... основной шаблон сайта ... #}

{% render_cookie_banner %}
</body>

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

Иначе получится декоративное окно: кнопки есть, а аналитика и внешние скрипты уже успели загрузиться до действия пользователя.

Программный интерфейс для клиентских приложений уже есть

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

Он подключается отдельным набором зависимостей:

pip install "django-consent-152fz[api]"

Затем добавляется приложение:

INSTALLED_APPS = [
    # ...
    "django_consent_152fz",
    "django_consent_152fz.api",
]

USE_API_152FZ = True

Дополнительная библиотека для программного интерфейса не является обязательной зависимостью ядра. Если она не нужна, базовый пакет остаётся легче.

Отдельно существует дополнительный контур подтверждённых и бумажных согласий:

INSTALLED_APPS = [
    # ...
    "django_consent_152fz",
    "django_consent_152fz.verified_consents",
]

Он нужен для сценариев, где подтверждения на сайте недостаточно и требуется загрузка или проверка подписанного документа.

Журнал событий и выгрузка

Одна из целей пакетов — не просто сохранить состояние, а сделать его пригодным для сопровождения.

В модуле согласий:

  • ConsentRecord отвечает на вопрос: какое состояние согласия сейчас;

  • ConsentEvent отвечает на вопрос: как система пришла к этому состоянию;

  • ConsentModuleOperationAuditLog хранит действия в административной панели и программном слое;

  • выбранные записи согласий и записи журнала операций можно выгружать в CSV.

В модуле файлов cookie:

  • CookieConsentRecord хранит итоговое состояние выбора категорий;

  • CookieConsentEvent хранит события выбора, обновления и устаревания;

  • выбранные события можно выгружать в CSV;

  • разделитель CSV задаётся через административные настройки модуля.

Полезная практическая формула:

ConsentRecord: какое состояние у согласия сейчас?
ConsentEvent: как система пришла к этому состоянию?
Журнал событий выбора файлов cookie в административной панели Django и выгрузка выбранных событий в CSV.
Журнал событий выбора файлов cookie в административной панели Django и выгрузка выбранных событий в CSV.

Стандартная административная панель Django используется намеренно. Для небольшого или среднего проекта отдельный интерфейс управления требованиями к обработке данных часто будет избыточным, а Django Admin уже даёт поиск, фильтры, разграничение доступа, действия над выбранными объектами и понятный интерфейс сопровождения.

Сценарий рассчитан прежде всего на просмотр и контроль истории. Исправление записей задним числом не должно быть штатной операцией.

Техническая совместимость

На момент публикации пакеты поддерживают:

  • Python 3.10–3.12;

  • Django 5.0–6.0;

  • лицензию MIT.

У модуля согласий ядро зависит только от Django. Дополнительный программный интерфейс подключается через [api], а ReportLab для формирования PDF-документов по сценариям подтверждённого или бумажного согласия — через [pdf].

Модуль файлов cookie остаётся независимым: для его установки, работы окна выбора, политики, реестра, журнала событий и выгрузок не требуются модуль согласий, дополнительный программный интерфейс или связанные с ними модели.

Чего пакеты не делают

Они не определяют:

  • какие персональные данные вправе собирать конкретный оператор;

  • какие цели обработки допустимы;

  • какой именно текст документа подходит для конкретного бизнеса;

  • какие организационные меры должны быть внедрены внутри компании;

  • когда требуется отдельное согласие и как оно должно быть юридически сформулировано.

Пакеты дают технический слой, чтобы согласованный процесс не остался набором чекбоксов в шаблонах и неясных полей в базе данных.

Дальнейшее развитие: подтверждённые согласия через «Госключ»

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

Одно из направлений развития — отдельная интеграция с приложением «Госключ».

Идея не в том, чтобы заменить обычные согласия на сайте электронной подписью. В большинстве пользовательских сценариев это было бы избыточно. Скорее речь о дополнительном режиме для случаев, где требуется подтверждённое подписание документа.

Технически такой контур мог бы работать так:

DocumentRevision
       ↓
формирование неизменяемого документа
       ↓
передача на подписание через «Госключ»
       ↓
получение результата подписания
       ↓
ConsentRecord + ConsentEvent + связанные файлы

В результате в записи согласия можно было бы хранить не только факт подтверждения в форме, но и сведения о подписанном документе, времени подписания, способе подтверждения и связанных результатах проверки.

Такую возможность разумно делать отдельным подключаемым приложением, а не частью базового модуля. Базовый django-consent-152fz должен оставаться пригодным для обычных сайтов без дополнительной инфраструктуры и сложного процесса подключения.

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

Что хотелось бы обсудить

Мне интересна обратная связь от Django-разработчиков, специалистов по защите информации, защите персональных данных и юристов, которые сталкивались с такими процессами в реальных проектах.

Особенно интересно узнать:

  • хватает ли текущего разделения на модуль согласий и модуль файлов cookie;

  • какие варианты выгрузки данных реально нужны чаще всего;

  • как удобнее встраивать сценарий получения согласия в многошаговые формы;

  • какие системы управления сайтами и внешние сервисы стоит поддержать в первую очередь;

  • насколько востребован отдельный контур подтверждённых согласий с подписанием через «Госключ»;

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

Исходный код, демонстрационные проекты, русская и английская документация доступны в репозитории проекта.