Django 4.1 release notes

August 3, 2022

Добро пожаловать в Django 4.1!

Эти заметки о выпуске охватывают new features, а также некоторые backwards incompatible changes, о которых вы должны знать при переходе с Django 4.0 или более ранней версии. Мы begun the deprecation process for some features.

См. руководство How to upgrade Django to a newer version, если вы обновляете существующий проект.

Совместимость с Python

Django 4.1 supports Python 3.8, 3.9, 3.10, and 3.11 (as of 4.1.3). We highly recommend and only officially support the latest release of each series.

Что нового в Django 4.1

Асинхронные обработчики для представлений на основе классов

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

import asyncio
from django.http import HttpResponse
from django.views import View

class AsyncView(View):
    async def get(self, request, *args, **kwargs):
        # Perform view logic using await.
        await asyncio.sleep(1)
        return HttpResponse("Hello async world!")

Более подробную информацию см. в разделе Asynchronous class-based views.

Асинхронный интерфейс ORM

QuerySet теперь предоставляет асинхронный интерфейс для всех операций доступа к данным. Они называются так же, как и существующие синхронные операции, но с префиксом a, например acreate(), aget() и так далее.

Новый интерфейс позволяет писать асинхронный код без необходимости оборачивать операции ORM в sync_to_async():

async for author in Author.objects.filter(name__startswith="A"):
    book = await author.books.afirst()

Обратите внимание, что на данном этапе базовые операции с базой данных остаются синхронными, при этом продолжается работа по внедрению асинхронной поддержки в компилятор SQL и интеграции драйверов асинхронных баз данных. Новый асинхронный интерфейс queryset в настоящее время инкапсулирует необходимые операции sync_to_async() для вас, и позволит вашему коду использовать преимущества развития асинхронной поддержки ORM по мере его развития.

Подробности и ограничения см. в разделе Асинхронные запросы.

Валидация ограничений

Ограничения Check, unique и exclusion, определенные в опции Meta.constraints, теперь проверяются во время model validation.

Доступность рендеринга формы

Чтобы помочь пользователям с программами чтения с экрана и другими вспомогательными технологиями, в этом выпуске доступны новые шаблоны форм на основе <div>. Они обеспечивают более доступную навигацию, чем старые шаблоны, и способны правильно группировать связанные элементы управления, такие как радиосписки, в наборы полей.

Новые шаблоны рекомендованы и станут стилем рендеринга формы по умолчанию при выводе формы, как {{ form }} в шаблоне, начиная с Django 5.0.

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

Подробную информацию см. в разделе the Forms section (below).

Незначительные особенности

django.contrib.admin

  • Админ dark mode CSS variables теперь применяется в отдельной таблице стилей и блоке шаблона.
  • ModelAdmin Фильтры списка, предоставляющие пользовательские подклассы FieldListFilter, теперь могут управлять разделителем значений строки запроса при фильтрации нескольких значений с помощью поиска __in.
  • Администратор history view теперь постранично.
  • В обертках связанных виджетов теперь есть ссылка на форму изменения объекта.
  • Метод AdminSite.get_app_list() теперь позволяет изменить порядок приложений и моделей на индексной странице администратора.

django.contrib.auth

  • Число итераций по умолчанию для хешера паролей PBKDF2 увеличено с 320 000 до 390 000.
  • Метод RemoteUserBackend.configure_user() теперь позволяет синхронизировать атрибуты пользователя с атрибутами в удаленной системе, такой как каталог LDAP.

django.contrib.gis

  • Новый метод GEOSGeometry.make_valid() позволяет преобразовывать недопустимые геометрии в допустимые.
  • Новый аргумент clone для GEOSGeometry.normalize() позволяет создать нормализованный клон геометрии.

django.contrib.postgres

  • Новая агрегатная функция BitXor() возвращает int побитового XOR всех не нулевых входных значений.
  • SpGistIndex теперь поддерживает покрывающие индексы на PostgreSQL 14+.
  • ExclusionConstraint теперь поддерживает охватывающие ограничения исключения с использованием индексов SP-GiST на PostgreSQL 14+.
  • Новый атрибут default_bounds в DateTimeRangeField и DecimalRangeField позволяет задавать границы для списков и кортежей.
  • ExclusionConstraint now allows specifying operator classes with the OpClass() expression.

django.contrib.sitemaps

  • Шаблон индекса карты сайта по умолчанию <sitemapindex> теперь включает метку времени <lastmod> там, где она доступна, с помощью нового метода get_latest_lastmod(). Пользовательские шаблоны индекса sitemap должны быть обновлены для корректировки context variables.

django.contrib.staticfiles

  • ManifestStaticFilesStorage теперь заменяет пути к ссылкам на карту источников CSS их хэшированными аналогами.

Database backends

  • Бэкенды баз данных сторонних производителей теперь могут указывать минимально необходимую версию базы данных с помощью атрибута DatabaseFeatures.minimum_database_version, который представляет собой кортеж (например, (10, 0) означает «10.0»). Если указана минимальная версия, бэкенды должны также реализовать DatabaseWrapper.get_database_version(), который возвращает кортеж текущей версии базы данных. Метод бэкенда DatabaseWrapper.init_connection_state() должен вызвать super() для того, чтобы проверка была выполнена.

Формы

  • Шаблон по умолчанию, используемый для отображения форм при приведении к строке, например, в шаблонах в виде {{ form }}, теперь настраивается на уровне проекта путем установки form_template_name на класс, предусмотренный для FORM_RENDERER.

    Form.template_name теперь является свойством, отсылающим к рендереру, но может быть переопределено строковым значением для указания имени шаблона per-form class.

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

  • Новый шаблон формы div.html, ссылающийся на атрибут Form.template_name_div, и соответствующий метод Form.as_div() выводят формы, используя элементы HTML <div>.

    Этот новый стиль вывода рекомендуется использовать вместо существующих стилей as_table(), as_p() и as_ul(), так как в шаблоне реализованы <fieldset> и <legend> для группировки связанных входов и более удобной навигации для пользователей программ чтения с экрана.

    Вывод на основе div станет стилем рендеринга по умолчанию, начиная с Django 5.0.

  • Для плавного перехода на новый стиль вывода <div> доступны два класса рендереров переходных форм: django.forms.renderers.DjangoDivFormRenderer и django.forms.renderers.Jinja2DivFormRenderer, для бэкендов шаблонов Django и Jinja2 соответственно.

    Вы можете применить один из них через настройку FORM_RENDERER. Например:

    FORM_RENDERER = "django.forms.renderers.DjangoDivFormRenderer"
    

    Как только стиль вывода <div> станет стилем по умолчанию, начиная с Django 5.0, эти переходные рендереры будут устаревшими и будут удалены в Django 6.0. Объявление FORM_RENDERER может быть удалено в это время.

  • Если новый стиль вывода <div> не подходит для вашего проекта, вам следует определить подкласс рендерера, указав form_template_name и formset_template_name для требуемого стиля, и установить FORM_RENDERER соответственно.

    Например, для стиля вывода <p>, используемого as_p(), вы определите рендерер формы, устанавливающий form_template_name в "django/forms/p.html" и formset_template_name в "django/forms/formsets/p.html".

  • Новый legend_tag() позволяет выводить метки полей в тегах <legend> через новый аргумент tag в label_tag().

  • Новый аргумент edit_only для modelformset_factory() и inlineformset_factory() позволяет предотвратить создание новых объектов.

  • T

  • Новые атрибуты BoundField.use_fieldset и Widget.use_fieldset помогают определить виджеты, в которых входы должны быть сгруппированы в <fieldset> с <legend>.

  • Аргумент error_messages для BaseFormSet теперь позволяет настраивать сообщения об ошибках для недопустимого количества форм, передавая ключи 'too_few_forms' и 'too_many_forms'.

  • IntegerField, FloatField и DecimalField теперь опционально принимают аргумент step_size. Он используется для установки HTML-атрибута step и проверяется при отправке формы.

Интернационализация

  • Функция i18n_patterns() теперь поддерживает языки со скриптами и регионами.

Команды управления

  • makemigrations --no-input теперь записывает в журнал ответы по умолчанию и причины, по которым миграции не могут быть созданы.
  • Новая опция makemigrations --scriptable перенаправляет вывод журнала и подсказки ввода в stderr, записывая в stdout только пути сгенерированных файлов миграции.
  • Новая опция migrate --prune позволяет удалять несуществующие миграции из таблицы django_migrations.
  • Файлы Python, созданные startproject, startapp, optimizemigration, makemigrations и squashmigrations, теперь форматируются с помощью команды black, если она присутствует на вашем PATH.
  • Новая команда optimizemigration позволяет оптимизировать операции для миграции.

Миграции

  • Новая операция RenameIndex позволяет переименовывать индексы, определенные в опциях Meta.indexes или index_together.
  • Автоопределитель миграций теперь генерирует операции RenameIndex вместо RemoveIndex и AddIndex при переименовании индексов, определенных в Meta.indexes.
  • Автоопределитель миграций теперь генерирует операции RenameIndex вместо AlterIndexTogether и AddIndex при перемещении индексов, определенных в Meta.index_together, в Meta.indexes.

Модели

  • Аргумент order_by выражения Window теперь принимает строковые ссылки на поля и преобразования.
  • Новая настройка CONN_HEALTH_CHECKS позволяет включить проверку здоровья для persistent database connections, чтобы уменьшить количество неудачных запросов, например, после перезапуска сервера базы данных.
  • QuerySet.bulk_create() теперь поддерживает обновление полей, когда при вставке строки не соблюдаются ограничения уникальности. Это поддерживается на MariaDB, MySQL, PostgreSQL и SQLite 3.24+.
  • QuerySet.iterator() теперь поддерживает предварительную выборку связанных объектов, если указан аргумент chunk_size. В старых версиях предварительная выборка не выполнялась.
  • <<<Объекты Q и наборы запросов теперь можно объединять, используя ^ в качестве оператора исключающего или (XOR). XOR поддерживается в MariaDB и MySQL. Для баз данных, не поддерживающих XOR, запрос будет преобразован в эквивалент с помощью AND, OR и NOT.
  • Новый атрибут Field.non_db_attrs позволяет настраивать атрибуты полей, которые не влияют на определение столбца.
  • В PostgreSQL столбцы AutoField, BigAutoField и SmallAutoField теперь создаются как столбцы идентичности, а не как последовательные столбцы с последовательностями.

Запросы и ответы

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

  • Новая настройка SECRET_KEY_FALLBACKS позволяет предоставить список значений для поворота секретного ключа.
  • Настройка SECURE_PROXY_SSL_HEADER теперь поддерживает список протоколов в значении заголовка, разделенный запятыми.

Сигналы

Шаблоны

  • Атрибут HTML <script> элемента id больше не требуется при обертывании фильтра шаблона json_script.
  • Теперь cached template loader включается в разработке, когда DEBUG является True, а OPTIONS['loaders'] не указан. Вы можете указать OPTIONS['loaders'], чтобы отменить это, если необходимо.

Тесты

  • DiscoverRunner теперь поддерживает параллельный запуск тестов на macOS, Windows и любых других системах, где по умолчанию используется метод запуска multiprocessing spawn.
  • Вложенный атомарный блок, помеченный как долговечный в django.test.TestCase, теперь вызывает RuntimeError, как и вне тестов.
  • SimpleTestCase.assertFormError() and assertFormsetError() now support passing a form/formset object directly.

URLs

  • Новый атрибут ResolverMatch.captured_kwargs хранит захваченные аргументы ключевых слов, как они были разобраны из URL.
  • Новый атрибут ResolverMatch.extra_kwargs хранит дополнительные аргументы ключевого слова, переданные функции представления.

Утилиты

  • SimpleLazyObject теперь поддерживает операции сложения.
  • mark_safe() теперь сохраняет ленивые объекты.

Валидаторы

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

Изменения в 4.1, несовместимые с обратными изменениями

API бэкенда базы данных

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

  • BaseDatabaseFeatures.has_case_insensitive_like изменено с True на False, чтобы отразить поведение большинства баз данных.
  • DatabaseIntrospection.get_key_columns() удаляется. Вместо этого используйте DatabaseIntrospection.get_relations().
  • Метод DatabaseOperations.ignore_conflicts_suffix_sql() заменяется на DatabaseOperations.on_conflict_suffix_sql(), который принимает аргументы fields, on_conflict, update_fields и unique_fields.
  • Аргумент ignore_conflicts метода DatabaseOperations.insert_statement() заменяется на on_conflict, который принимает django.db.models.constants.OnConflict.
  • DatabaseOperations._convert_field_to_tz() заменяется на DatabaseOperations._convert_sql_to_tz(), который принимает аргументы sql, params и tzname.
  • Несколько методов даты и времени на DatabaseOperations теперь принимают аргументы sql и params вместо field_name и возвращают кортеж, содержащий некоторый SQL и параметры, которые должны быть интерполированы в этот SQL. Измененные методы имеют новые сигнатуры:
    • DatabaseOperations.date_extract_sql(lookup_type, sql, params)
    • DatabaseOperations.datetime_extract_sql(lookup_type, sql, params, tzname)
    • DatabaseOperations.time_extract_sql(lookup_type, sql, params)
    • DatabaseOperations.date_trunc_sql(lookup_type, sql, params, tzname=None)
    • DatabaseOperations.datetime_trunc_sql(self, lookup_type, sql, params, tzname)
    • DatabaseOperations.time_trunc_sql(lookup_type, sql, params, tzname=None)
    • DatabaseOperations.datetime_cast_date_sql(sql, params, tzname)
    • DatabaseOperations.datetime_cast_time_sql(sql, params, tzname)

django.contrib.gis

  • Удалена поддержка GDAL 2.1.
  • Удалена поддержка PostGIS 2.4.

Dropped support for PostgreSQL 10

Upstream support for PostgreSQL 10 ends in November 2022. Django 4.1 supports PostgreSQL 11 and higher.

Прекращена поддержка MariaDB 10.2

Поддержка MariaDB 10.2 заканчивается в мае 2022 года. Django 4.1 поддерживает MariaDB 10.3 и выше.

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

Поиск в списке изменений администратора с использованием нескольких поисковых запросов теперь выполняется в одном вызове filter(), а не в последовательных вызовах filter().

Для многозначных отношений это означает, что строки из связанной модели должны соответствовать всем терминам, а не любому из них. Например, если search_fields установлен на ['child__name', 'child__age'], а пользователь ищет 'Jamal 17', строки о родителях будут возвращены только в том случае, если есть связь с каким-то 17-летним ребенком по имени Джамал, вместо того, чтобы возвращать родителям, у которых просто есть младший или старший ребенок по имени Джамал в дополнение к какому-то другому 17-летнему.

См. тему Охватывающие многозначные отношения для более подробного обсуждения этого различия. В Django 4.0 и более ранних версиях get_search_results() следовал второму примеру запроса, но это недокументированное поведение приводило к запросам с избыточными соединениями.

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

Чтобы унифицировать поведение с отношениями «многие ко многим» для несохраненных экземпляров модели, обратный внешний ключ теперь поднимает ValueError при вызове related managers для несохраненных объектов.

Разное

  • Related managers for ForeignKey, ManyToManyField, and GenericRelation are now cached on the Model instance to which they belong. This change was reverted in Django 4.1.2.
  • Бегунок тестирования Django теперь возвращает ненулевой код ошибки при неожиданных успехах тестов, помеченных unittest.expectedFailure().
  • CsrfViewMiddleware больше не маскирует CSRF cookie, как это делает CSRF токен в DOM.
  • CsrfViewMiddleware теперь использует request.META['CSRF_COOKIE'] для хранения незамаскированного CSRF-секрета, а не замаскированной версии. Это недокументированный, частный API.
  • Атрибуты ModelAdmin.actions и inlines теперь по умолчанию используют пустой кортеж, а не пустой список, чтобы предотвратить непреднамеренную мутацию.
  • Атрибут type="text/css" больше не включается в теги <link> для CSS form media.
  • События JavaScript formset:added и formset:removed теперь являются событиями чистого JavaScript и не зависят от jQuery. Более подробно об этом изменении смотрите События встроенной формы.
  • Аргумент exc_info недокументированной функции django.utils.log.log_response() заменяется на exception.
  • Аргумент size недокументированной функции django.views.static.was_modified_since() удален.
  • В пользовательском интерфейсе выхода из системы администратора теперь используются запросы POST.
  • Недокументированное свойство InlineAdminFormSet.non_form_errors заменено методом non_form_errors(). Это соответствует BaseFormSet.
  • Согласно above, загрузчик кэшированных шаблонов теперь включен в разработке. Вы можете указать OPTIONS['loaders'], чтобы отменить это, если необходимо.
  • Недокументированный миксин django.contrib.auth.views.SuccessURLAllowedHostsMixin заменен на RedirectURLMixin.
  • Подклассы BaseConstraint должны реализовать метод validate(), чтобы эти ограничения можно было использовать для проверки.
  • Недокументированные URLResolver._is_callback(), URLResolver._callback_strs и URLPattern.lookup_str() перемещены в django.contrib.admindocs.utils.
  • Метод Model.full_clean() теперь преобразует значение exclude в set. Также предпочтительнее передавать значение exclude в виде set методам Model.clean_fields(), Model.full_clean(), Model.validate_unique() и Model.validate_constraints().
  • Минимальная поддерживаемая версия asgiref увеличена с 3.4.1 до 3.5.2.
  • Комбинированные выражения больше не используют опасное для ошибок поведение угадывания output_field при совпадении типов аргументов. Как следствие, разрешение output_field для функций базы данных и комбинированных выражений теперь может привести к сбою при смешанных типах. В таких случаях необходимо явно задать output_field.

Функции, устаревшие в версии 4.1

Выход из системы через GET

Выход из системы через GET запросы к built-in logout view устарел. Вместо этого используйте запросы POST.

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

<form id="logout-form" method="post" action="{% url 'admin:logout' %}">
  {% csrf_token %}
  <button type="submit">{% translate "Log out" %}</button>
</form>
#logout-form {
  display: inline;
}
#logout-form button {
  background: none;
  border: none;
  cursor: pointer;
  padding: 0;
  text-decoration: underline;
}

Разное

  • Контекст для шаблонов индексов sitemap, представляющий собой плоский список URL-адресов, устарел. Пользовательские шаблоны индекса sitemap должны быть обновлены для настроенного context variables, ожидая список объектов с location и необязательными атрибутами lastmod.

  • Переходная установка CSRF_COOKIE_MASKED устарела.

  • Аргумент name в django.utils.functional.cached_property() является устаревшим, так как начиная с Python 3.6 в нем нет необходимости.

  • Аргумент opclasses в django.contrib.postgres.constraints.ExclusionConstraint устарел в пользу использования OpClass() в ExclusionConstraint.expressions. Чтобы использовать его, вам нужно добавить 'django.contrib.postgres' в ваш INSTALLED_APPS.

    После внесения этого изменения makemigrations создаст новую миграцию с двумя операциями: RemoveConstraint и AddConstraint. Поскольку это изменение не влияет на схему базы данных, операцию SeparateDatabaseAndState можно использовать только для обновления состояния миграции без выполнения какого-либо SQL. Переместите сгенерированные операции в аргумент state_operations в SeparateDatabaseAndState. Например:

    class Migration(migrations.Migration):
        ...
    
        operations = [
            migrations.SeparateDatabaseAndState(
                database_operations=[],
                state_operations=[
                    migrations.RemoveConstraint(
                        ...
                    ),
                    migrations.AddConstraint(
                        ...
                    ),
                ],
            ),
        ]
    
  • The undocumented ability to pass errors=None to SimpleTestCase.assertFormError() and assertFormsetError() is deprecated. Use errors=[] instead.

  • django.contrib.sessions.serializers.PickleSerializer является устаревшим из-за риска удаленного выполнения кода.

  • Использование QuerySet.iterator() в наборе запросов, который выполняет предварительную выборку связанных объектов без предоставления аргумента chunk_size, устарело. В старых версиях предварительная выборка не производилась. Предоставление значения для chunk_size означает, что дополнительный запрос на чанк, необходимый для предварительной выборки, желателен.

  • Передача несохраненных экземпляров модели в связанные фильтры устарела. В Django 5.0 будет возникать исключение.

  • created=True добавляется к сигнатуре RemoteUserBackend.configure_user(). Поддержка подклассов RemoteUserBackend, которые не принимают этот аргумент, устарела.

  • Псевдоним django.utils.timezone.utc для datetime.timezone.utc устарел. Используйте datetime.timezone.utc напрямую.

  • Передача объекта ответа и имени формы/формсета в SimpleTestCase.assertFormError() и assertFormsetError() устарела. Используйте:

    assertFormError(response.context['form_name'], …)
    assertFormsetError(response.context['formset_name'], …)
    

    или передать объект формы/formset напрямую.

  • Недокументированный django.contrib.gis.admin.OpenLayersWidget является устаревшим.

  • django.contrib.auth.hashers.CryptPasswordHasher устарел.

  • Возможность передавать nulls_first=False или nulls_last=False в методы Expression.asc() и Expression.desc(), а также выражение OrderBy является устаревшей. Вместо этого используйте None.

  • Шаблоны "django/forms/default.html" и "django/forms/formsets/default.html", которые являются прокси для шаблонов на основе таблиц, устарели. Вместо них используйте конкретный шаблон.

  • Недокументированный метод LogoutView.get_next_page() переименован в get_success_url().

Функции, удаленные в 4.1

Эти функции достигли конца своего цикла устаревания и будут удалены в Django 4.1.

Подробнее об этих изменениях, в том числе о том, как удалить использование этих функций, смотрите Функции, устаревшие в версии 3.2.

  • Устранена поддержка присвоения объектов, не поддерживающих создание глубоких копий с помощью copy.deepcopy(), атрибутам класса в TestCase.setUpTestData().
  • Удалена поддержка использования булевого значения в BaseCommand.requires_system_checks.
  • Аргумент whitelist и атрибут domain_whitelist в django.core.validators.EmailValidator удаляются.
  • Переменная конфигурации приложения default_app_config удалена.
  • TransactionTestCase.assertQuerysetEqual() больше не вызывает repr() на queryset при сравнении со строковыми значениями.
  • Бэкенд django.core.cache.backends.memcached.MemcachedCache удален.
  • Убрана поддержка формата сообщений, существовавшего до версии Django 3.2 и использовавшегося django.contrib.messages.storage.cookie.CookieStorage.
Вернуться на верх