Django 6.1.1 — исправлены регрессии ORM, Admin и миграций

Django 6.1.1 был выпущен 2 сентября 2026 года. Обновление устранило 12 ошибок, преимущественно связанных с регрессиями ветки 6.1. Изменения затронули ORM, Django Admin, миграции, шаблоны, формы и SQLite. Для проектов на Django 6.1 это обновление особенно важно при работе с несколькими базами данных, использовании Prefetch, кастомном Model.from_db() и сложных запросах.

Django 6.1.1
Django 6.1.1

Django 6.1.1 сосредоточен на исправлении регрессий

Django 6.1.1 — первый корректирующий релиз после выхода Django 6.1 от 5 августа 2026 года. Согласно официальным release notes, новая версия исправляет один сбой, присутствовавший начиная с Django 5.2, и ряд ошибок, обнаруженных в Django 6.1.

Новых возможностей в 6.1.1 нет. Это именно patch-релиз, задача которого — стабилизировать изменения Django 6.1 после первого месяца эксплуатации в реальных проектах.

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

Всего в release notes перечислено 12 исправлений:

ОбластьЧто исправленоПрактический эффект
ORMdistinct(*fields) вместе с order_by() и values() больше не падает при двух lookup-путях к одной колонкеСложные запросы снова выполняются корректно
TemplatesСтроковые и переводимые литералы с .. больше не попадают под проверку устаревших double-dot lookupШаблоны вроде {{ "a..b" }} не вызывают ложную ошибку
Django AdminModelAdmin.list_display с несколькими переходами через __ больше не падает и не берет значение не с той моделиИсправлено отображение связанных полей в списках админки
ModelsПроверка fields.E323 теперь обнаруживает смешанные варианты on_delete в автоматически созданных промежуточных моделях ManyToManyFieldОшибочные комбинации обнаруживаются системной проверкой
ORM / multi-DBКастомный queryset в Prefetch для ForeignKey и обратного OneToOneField снова наследует маршрутизацию базы от родительского querysetЗапросы не уходят ошибочно в default вместо выбранной БД
FormsHTML-safe строки в media-ресурсах снова выводятся как готовая разметкаmark_safe() больше не интерпретируется как путь к статическому файлу
MigrationsИзменение только Python-варианта on_delete больше не запускает лишнее изменение схемыМеньше ненужного DDL и блокировок таблиц
SQLiteDecimalField без max_digits и decimal_places больше не вызывает сбой при чтении значенияИсправлен редкий, но реальный сценарий работы с SQLite
ORM APIСтарые переопределения Model.from_db() без fetch_mode снова работаютУстранен TypeError после перехода на 6.1
Django AdminИсправлен поиск по search_fields с __exact, choices и BooleanFieldПоиск больше не падает и не возвращает ложные совпадения
ORM__in и __range для аннотаций корректно принимают итераторыИтератор больше не исчерпывается до построения SQL
ORMin_bulk() после values() и values_list() сохраняет выбранные аннотации и корректные ключиРезультат больше не теряет данные из проекции

ORM получил наиболее существенные исправления

Django 6.1.1 закрывает сразу несколько регрессий ORM, способных влиять на корректность данных, а не только на стабильность приложения. На практике это самая важная часть релиза для backend-разработчиков.

Prefetch снова корректно работает с несколькими базами

Одна из наиболее неприятных регрессий Django 6.1 проявлялась при использовании Prefetch с кастомным queryset для прямого ForeignKey или обратного OneToOneField.

Если основной queryset выполнялся через конкретный alias базы данных, например replica, связанный кастомный queryset мог потерять этот контекст и обратиться к default. В тикете #37300 показан сценарий, где основной объект загружается из replica, а связанный объект — уже из default.

Это могло привести к двум вариантам поведения:

  • приложение незаметно читало данные не из той базы;
  • Django выбрасывал ValueError, если database router запрещал связывать объекты, полученные из разных БД.

В 6.1.1 подсказка о родительском экземпляре снова передается кастомному queryset, поэтому маршрутизация возвращена к поведению Django 6.0.

Для проектов с primary/replica, шардингом или собственными database routers это одно из главных оснований обновиться без откладывания.

Итераторы в __in и __range больше не теряются

В Django 6.1 появился дефект при передаче итератора в __in или __range, если левая часть lookup была аннотацией или alias.

Проблема заключалась в двойном обходе правой части выражения. Первый проход исчерпывал iterator, после чего второй уже не получал значений. Для __in это могло дать пустой queryset, а __range — завершиться ValueError.

Django 6.1.1 устраняет эту регрессию. Обычные lookup по полям при этом не были затронуты — ошибка относилась именно к пути через annotation или alias.

in_bulk() больше не отбрасывает аннотации

Еще одна ошибка затрагивала новую возможность комбинировать QuerySet.in_bulk() с values() и values_list().

Если queryset содержал аннотацию, а поле, используемое in_bulk() как ключ словаря, отсутствовало в выбранной проекции, Django внутренне перестраивал список полей. При этом выбранные аннотации могли исчезнуть из результата.

После обновления до 6.1.1 in_bulk() сохраняет аннотации и формирует правильные ключи. Это особенно актуально для кода, где ORM используется как слой подготовки компактных словарей или кортежей без создания полных экземпляров моделей.

Исправления Django Admin устраняют сбои и неверные значения

Django 6.1.1 исправляет две регрессии административной панели: работу связанных полей в list_display и поиск через search_fields с точными lookup.

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

В Django 6.1 такой сценарий мог завершиться ошибкой или, что хуже, показать значение не из той модели, если одинаковое имя поля встречалось на разных уровнях связи. В тикете #37270 этот дефект был классифицирован как release blocker.

Отдельно исправлен поиск в changelist:

  • search_fields с __exact по полю с choices мог приводить к сбою;
  • __exact для BooleanField мог ошибочно сопоставлять любой поисковый термин со всеми строками, где значение равно True.

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

Model.from_db() получил переходную совместимость

Django 6.1 добавил fetch modes для управления поведением полей, данные которых не были загружены первоначальным запросом. Одновременно в вызов Model.from_db() появился новый keyword-аргумент fetch_mode.

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

@classmethod
def from_db(cls, db, field_names, values):
    ...

После перехода на Django 6.1 любой запрос к такой модели мог завершиться ошибкой:

TypeError: MyModel.from_db() got an unexpected keyword argument 'fetch_mode'

Django 6.1.1 восстанавливает совместимость: фреймворк определяет, поддерживает ли пользовательское переопределение fetch_mode, и для старой сигнатуры вызывает метод без нового аргумента.

При этом старый вариант считается устаревающим. В тикете #37259 указано, что Django генерирует RemovedInDjango70Warning, поэтому собственные реализации лучше обновить заранее:

@classmethod
def from_db(cls, db, field_names, values, *, fetch_mode=None):
    ...

Таким образом, 6.1.1 снимает немедленную проблему совместимости, но не отменяет необходимость адаптировать пользовательский код до Django 7.0.

Миграции on_delete снова избегают лишнего DDL

Django 6.1 расширил работу с удалением связанных объектов на уровне базы данных, но изменение затронуло и обычные Python-варианты on_delete.

В результате миграция, меняющая, например, CASCADE на PROTECT, могла выполнять реальную операцию изменения схемы, хотя на уровне БД такая смена не требовала DDL. На большинстве СУБД это означало удаление и повторное создание внешнего ключа, а на SQLite — потенциальную перестройку всей таблицы.

Исправление #37260 возвращает прежнюю логику:

  • переход между Python-level вариантами on_delete снова является database no-op;
  • переход к новым database-level вариантам или между ними по-прежнему изменяет схему там, где это требуется.

Для крупных таблиц это существенная разница. Лишний DDL может увеличить время deploy, удерживать блокировки и создавать ненужную нагрузку на production-базу.

SQLite, шаблоны и form media тоже получили исправления

Несколько изменений Django 6.1.1 закрывают более узкие сценарии, но каждый из них способен проявиться в production или тестовом окружении.

DecimalField без явно заданных max_digits и decimal_places больше не приводит к сбою при получении значений из SQLite. Такой сценарий встречается реже стандартной декларации decimal-полей, однако SQLite часто используется локально и в автоматических тестах, поэтому дефект мог ломать CI даже у проектов с другой production-СУБД.

Шаблонизатор перестал ошибочно применять deprecation double-dot lookup к строковым литералам с двумя точками подряд. Конструкция вида:

{{ "a..b" }}

снова рассматривается как обычная строка, а не как запрещаемая форма обращения к переменной.

Исправлена и работа form media. HTML-safe строки, созданные, например, через mark_safe(), в Django 6.1 могли ошибочно трактоваться как путь к asset. В 6.1.1 такие значения снова рендерятся непосредственно как безопасная HTML-разметка.

Обновление до Django 6.1.1

Django 6.1.1 опубликован на PyPI 2 сентября 2026 года. Пакет требует Python 3.12 или новее, а ветка Django 6.1 официально поддерживает Python 3.12, 3.13 и 3.14.

Для существующего проекта на Django 6.1 обновление выполняется обычным способом:

python -m pip install --upgrade "Django==6.1.1"

На Windows официальный сайт также приводит вариант:

py -m pip install "Django==6.1.1"

После обновления имеет смысл прогнать тесты прежде всего для следующих участков:

  1. queryset с Prefetch() и несколькими подключениями к БД;
  2. собственные переопределения Model.from_db();
  3. Django Admin с цепочками связей в list_display и сложными search_fields;
  4. запросы с annotations/aliases и __in, __range, in_bulk();
  5. миграции, меняющие on_delete;
  6. тесты на SQLite, если в моделях используются нестандартно определенные DecimalField.

Вывод

Версия Django 6.1.1 не относится к разряду security-релизов: в официальных release notes отсутствуют упоминания CVE или отдельный раздел с исправлением уязвимостей. Основное преимущество этого обновления заключается в устранении регрессий, возникших после перехода на Django 6.1. Некоторые из этих регрессий касались корректности запросов и функционирования баз данных.

Наивысший приоритет обновление имеет для проектов, которые уже используют Django 6.1 и работают с multi-DB, Prefetch, сложным ORM, кастомным Model.from_db() или насыщенной административной панелью. Даже если эти сценарии не используются, оставаться на 6.1.0 особого смысла нет: официальная страница загрузки Django прямо рекомендует переходить на последний patch-релиз соответствующей ветки и указывает Django 6.1.1 как текущую стабильную версию серии 6.1.

При этом обновление не требует освоения новых API. Основная задача после установки — прогнать тестовый набор и обратить внимание на предупреждение для старой сигнатуры Model.from_db(): совместимость восстановлена временно, а пользовательский код следует подготовить к будущему удалению старого варианта в Django 7.0.

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

Вам также может понравиться