Примечания к выпуску Django 4.1

3 августа 2022 года

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

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

См. руководство Как обновить Django до более новой версии, если вы обновляете существующий проект.

Совместимость с 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!")

Более подробную информацию см. в разделе Асинхронные представления на основе классов.

Асинхронный интерфейс 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 теперь позволяет указывать классы операторов с помощью выражения OpClass().

django.contrib.sitemaps

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

django.contrib.staticfiles

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

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

  • Бэкенды баз данных сторонних производителей теперь могут указывать минимально необходимую версию базы данных с помощью атрибута 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() позволяет предотвратить создание новых объектов.

  • Атрибуты класса js и css Media теперь позволяют использовать хешируемые объекты, а не только строки пути, если эти объекты реализуют __html__ () (обычно при декорировании с помощью декоратора html_safe()).

  • Новые атрибуты 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() и assertFormsetError() теперь поддерживают передачу объекта формы/формсета напрямую.

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.

Прекращена поддержка PostgreSQL 10

Поддержка PostgreSQL 10 заканчивается в ноябре 2022 года. Django 4.1 поддерживает PostgreSQL 11 и выше.

Прекращена поддержка 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(
                        ...
                    ),
                ],
            ),
        ]
    
  • Недокументированная возможность передавать errors=None в SimpleTestCase.assertFormError() и assertFormsetError() устарела. Вместо этого используйте errors=[].

  • 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.
Вернуться на верх