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

4 декабря 2023

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

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

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

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

Django 5.0 поддерживает Python 3.10, 3.11 и 3.12. Мы настоятельно рекомендуем и официально поддерживаем только последний релиз каждой серии.

Серия Django 4.2.x является последней, поддерживающей Python 3.8 и 3.9.

Поддержка сторонних библиотек для старых версий Django

После выхода Django 5.0 мы предлагаем авторам сторонних приложений отказаться от поддержки всех версий Django, предшествующих 4.2. В это время вы должны иметь возможность запускать тесты вашего пакета, используя python -Wd, чтобы появились предупреждения об устаревании. После исправления предупреждений об устаревании ваше приложение должно быть совместимо с Django 5.0.

Что нового в Django 5.0

Фасетные фильтры в админке

Количество фасетов теперь отображается для примененных фильтров в списке изменений в админке при включении через пользовательский интерфейс. Это поведение можно изменить с помощью нового атрибута ModelAdmin.show_facets. Для получения дополнительной информации смотрите Грани.

Упрощенные шаблоны для отрисовки полей формы

В Django 5.0 появилось понятие группы полей и шаблоны групп полей. Это упрощает отрисовку связанных элементов поля формы Django, таких как его метка, виджет, текст подсказки и ошибки.

Например, шаблон ниже:

<form>
...
<div>
  {{ form.name.label_tag }}
  {% if form.name.help_text %}
    <div class="helptext" id="{{ form.name.auto_id }}_helptext">
      {{ form.name.help_text|safe }}
    </div>
  {% endif %}
  {{ form.name.errors }}
  {{ form.name }}
  <div class="row">
    <div class="col">
      {{ form.email.label_tag }}
      {% if form.email.help_text %}
        <div class="helptext" id="{{ form.email.auto_id }}_helptext">
          {{ form.email.help_text|safe }}
        </div>
      {% endif %}
      {{ form.email.errors }}
      {{ form.email }}
    </div>
    <div class="col">
      {{ form.password.label_tag }}
      {% if form.password.help_text %}
        <div class="helptext" id="{{ form.password.auto_id }}_helptext">
          {{ form.password.help_text|safe }}
        </div>
      {% endif %}
      {{ form.password.errors }}
      {{ form.password }}
    </div>
  </div>
</div>
...
</form>

Теперь это можно упростить до:

<form>
...
<div>
  {{ form.name.as_field_group }}
  <div class="row">
    <div class="col">{{ form.email.as_field_group }}</div>
    <div class="col">{{ form.password.as_field_group }}</div>
  </div>
</div>
...
</form>

<<<По умолчанию as_field_group() отображает поля с шаблоном "django/forms/field.html" и может быть настроен для каждого проекта, каждого поля или каждого запроса. Смотрите Шаблоны групп полей многократного использования.

Значения по умолчанию, рассчитанные на основе базы данных

Новый параметр Field.db_default устанавливает значение по умолчанию, вычисленное по базе данных. Например:

from django.db import models
from django.db.models.functions import Now, Pi


class MyModel(models.Model):
    age = models.IntegerField(db_default=18)
    created = models.DateTimeField(db_default=Now())
    circumference = models.FloatField(db_default=2 * Pi())

Поле модели, созданное в базе данных

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

from django.db import models
from django.db.models import F


class Square(models.Model):
    side = models.IntegerField()
    area = models.GeneratedField(
        expression=F("side") * F("side"),
        output_field=models.BigIntegerField(),
        db_persist=True,
    )

Больше возможностей для объявления вариантов полей

Field.choices (для полей модели) и ChoiceField.choices (для полей формы) позволяют более гибко подходить к объявлению их значений. В предыдущих версиях Django, choices должен был быть либо списком из двух кортежей, либо подклассом Типы перечисления, но последний требовал обращения к атрибуту .choices для предоставления значений в ожидаемой форме:

from django.db import models

Medal = models.TextChoices("Medal", "GOLD SILVER BRONZE")

SPORT_CHOICES = [
    ("Martial Arts", [("judo", "Judo"), ("karate", "Karate")]),
    ("Racket", [("badminton", "Badminton"), ("tennis", "Tennis")]),
    ("unknown", "Unknown"),
]


class Winner(models.Model):
    name = models.CharField(...)
    medal = models.CharField(..., choices=Medal.choices)
    sport = models.CharField(..., choices=SPORT_CHOICES)

В Django 5.0 добавлена поддержка приема отображения или callable вместо iterable, а также больше не требуется использовать .choices напрямую для расширения enumeration types:

from django.db import models

Medal = models.TextChoices("Medal", "GOLD SILVER BRONZE")

SPORT_CHOICES = {  # Using a mapping instead of a list of 2-tuples.
    "Martial Arts": {"judo": "Judo", "karate": "Karate"},
    "Racket": {"badminton": "Badminton", "tennis": "Tennis"},
    "unknown": "Unknown",
}


def get_scores():
    return [(i, str(i)) for i in range(10)]


class Winner(models.Model):
    name = models.CharField(...)
    medal = models.CharField(..., choices=Medal)  # Using `.choices` not required.
    sport = models.CharField(..., choices=SPORT_CHOICES)
    score = models.IntegerField(choices=get_scores)  # A callable is allowed.

Под капотом предоставленные choices нормализуются в список из 2 кортежей в качестве канонической формы каждый раз, когда значение choices обновляется. Для получения дополнительной информации, пожалуйста, ознакомьтесь с model field reference on choices.

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

django.contrib.admin

  • Новый метод AdminSite.get_log_entries() позволяет настраивать набор запросов для перечисленных записей журнала сайта.
  • Фильтры администратора django.contrib.admin.AllValuesFieldListFilter, ChoicesFieldListFilter, RelatedFieldListFilter и RelatedOnlyFieldListFilter теперь обрабатывают многозначные параметры запроса.
  • XRegExp обновлен с версии 3.2.0 до 5.1.1.
  • Метод new AdminSite.get_model_admin() возвращает класс администратора для заданного класса модели.
  • Свойства в ModelAdmin.list_display теперь поддерживают атрибут boolean.
  • jQuery обновлен с версии 3.6.4 до 3.7.1.

django.contrib.auth

django.contrib.contenttypes

django.contrib.gis

  • Новая функция ClosestPoint() возвращает двумерную точку на геометрии, которая находится ближе всего к другой геометрии.
  • GIS aggregates теперь поддерживает аргумент filter.
  • Добавлена поддержка GDAL 3.7 и GEOS 3.12.
  • Новый метод GEOSGeometry.equals_identical() позволяет проверять геометрии на эквивалентность по точкам.

django.contrib.messages

django.contrib.postgres

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

  • В ASGI теперь обрабатываются события http.disconnect. Это позволяет представлениям выполнять необходимую очистку, если клиент отключается до того, как будет сгенерирован ответ. Дополнительные сведения см. в разделе Обработка разъединений.

Декораторы

Отчеты об ошибках

Хранение файлов

  • File.open() теперь передает все позиционные (*args) и ключевые аргументы (**kwargs) встроенному в Python open().

Формы

  • Новый аргумент assume_scheme для URLField позволяет указать схему URL по умолчанию.
  • Для улучшения доступности внесены следующие изменения:
    • Поля формы теперь включают HTML-атрибут aria-describedby, чтобы программы чтения с экрана могли ассоциировать поля формы с текстом подсказки.
    • Недействительные поля формы теперь включают HTML-атрибут aria-invalid="true".

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

  • Теперь доступны поддержка и перевод на уйгурский язык.

Миграции

  • Сериализация функций, украшенных символами functools.cache() или functools.lru_cache(), теперь поддерживается без необходимости написания собственного сериализатора.

Модели

  • Новый аргумент create_defaults методов QuerySet.update_or_create() и QuerySet.aupdate_or_create() позволяет указывать различные значения полей для операции создания.
  • Новый атрибут violation_error_code для BaseConstraint, CheckConstraint и UniqueConstraint позволяет настраивать code из ValidationError, поднимаемых во время model validation.
  • Аргумент force_insert в Model.save() теперь позволяет указать кортеж родительских классов, которые должны быть принудительно вставлены.
  • <<<Методы QuerySet.bulk_create() и QuerySet.abulk_create() теперь устанавливают первичный ключ для каждого экземпляра модели, если включен параметр update_conflicts (если база данных поддерживает его).
  • Новый атрибут UniqueConstraint.nulls_distinct позволяет настраивать обработку значений NULL на PostgreSQL 15+.
  • Новые асинхронные ярлыки aget_object_or_404() и aget_list_or_404() позволяют асинхронно получать объекты.
  • Новая функция aprefetch_related_objects() позволяет осуществлять асинхронную предварительную выборку экземпляров моделей.
  • QuerySet.aiterator() теперь поддерживает предыдущие вызовы prefetch_related().
  • В MariaDB 10.7+ столбец UUIDField теперь создается как столбец UUID, а не как столбец CHAR(32). Более подробную информацию о Перенос существующих UUIDField на MariaDB 10.7+ см. в руководстве по миграции выше.
  • Django теперь поддерживает oracledb версии 1.3.2 или выше. Поддержка cx_Oracle устарела с этого релиза и будет удалена в Django 6.0.

Пагинация

Сигналы

  • Новые методы Signal.asend() и Signal.asend_robust() позволяют асинхронно отправлять сигналы. Приемники сигналов могут быть синхронными или асинхронными, и они будут автоматически адаптированы к правильному стилю вызова.

Шаблоны

  • Новый фильтр шаблона escapeseq применяет escape к каждому элементу последовательности.

Тесты

  • Client и AsyncClient теперь предоставляют асинхронные методы, используя префикс a: asession(), alogin(), aforce_login() и alogout().
  • AsyncClient теперь поддерживает параметр follow.
  • Новая опция test --durations позволяет показать продолжительность самых медленных тестов на Python 3.12+.

Валидаторы

  • Новый аргумент offset в StepValueValidator позволяет указать смещение для допустимых значений.

Изменения в 5.0, несовместимые с обратным ходом событий

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

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

  • DatabaseFeatures.supports_expression_defaults следует установить значение False, если база данных не поддерживает использование функций базы данных в качестве значений по умолчанию.
  • DatabaseFeatures.supports_default_keyword_in_insert следует установить значение False, если база данных не поддерживает ключевое слово DEFAULT в запросах INSERT.
  • DatabaseFeatures.supports_default_keyword_in_bulk_insert следует установить значение False, если база данных не поддерживает ключевое слово DEFAULT в массовых INSERT запросах.

django.contrib.gis

  • Поддержка GDAL 2.2 и 2.3 удалена.
  • Поддержка GEOS 3.6 и 3.7 удалена.

django.contrib.sitemaps

  • Функция django.contrib.sitemaps.ping_google() и команда управления ping_google удалены, так как конечная точка Google Sitemaps ping устарела и будет удалена в январе 2024 года.
  • Класс исключений django.contrib.sitemaps.SitemapNotFound удален.

Прекращена поддержка MySQL < 8.0.11

Убрана поддержка предварительных выпусков MySQL серии 8.0.x. Django 5.0 поддерживает MySQL 8.0.11 и выше.

Использование create_defaults__exact теперь может потребоваться при использовании QuerySet.update_or_create().

QuerySet.update_or_create() теперь поддерживает параметр create_defaults. Как следствие, все модели, имеющие поле с именем create_defaults, которое используется с update_or_create(), должны указывать это поле в поиске с помощью create_defaults__exact.

Перенос существующих UUIDField на MariaDB 10.7+

В MariaDB 10.7+, UUIDField теперь создается как UUID столбец, а не CHAR(32). Как следствие, любой UUIDField, созданный в Django < 5.0, должен быть заменен на подкласс UUIDField, поддерживаемый CHAR(32):

class Char32UUIDField(models.UUIDField):
    def db_type(self, connection):
        return "char(32)"

Например:

class MyModel(models.Model):
    uuid = models.UUIDField(primary_key=True, default=uuid.uuid4)

Должно стать:

class Char32UUIDField(models.UUIDField):
    def db_type(self, connection):
        return "char(32)"


class MyModel(models.Model):
    uuid = Char32UUIDField(primary_key=True, default=uuid.uuid4)

Выполнение команды makemigrations приведет к созданию миграции, содержащей безоперационную операцию AlterField.

Разное

  • Аргумент instance недокументированного метода BaseModelFormSet.save_existing() переименован в obj.
  • Недокументированный django.contrib.admin.helpers.checkbox удален.
  • Целочисленные поля теперь проверяются как 64-битные целые числа на SQLite, чтобы соответствовать поведению sqlite3.
  • Недокументированный атрибут Query.annotation_select_mask заменен с набора строк на упорядоченный список строк.
  • ImageField.update_dimension_fields() больше не вызывается по сигналу post_init, если width_field и height_field не установлены.
  • <<<Функция базы данных Now теперь использует LOCALTIMESTAMP вместо CURRENT_TIMESTAMP на Oracle.
  • AdminSite.site_header теперь отображается в теге <div> вместо <h1>. Пользователи программ чтения с экрана полагаются на элементы заголовков для навигации по странице. Наличие двух элементов <h1> приводило к путанице, а заголовок сайта не помогал, так как повторялся на всех страницах.
  • Чтобы улучшить доступность, область основного содержимого админки и область содержимого заголовка теперь отображаются в тегах <main> и <header> вместо <div>.
  • В базах данных без встроенной поддержки оператора SQL XOR, ^ как исключительный оператор или (XOR) теперь возвращает строки, которые соответствуют нечетному числу операндов, а не одному операнду. Это соответствует поведению MySQL, MariaDB и Python.
  • Минимальная поддерживаемая версия asgiref увеличена с 3.6.0 до 3.7.0.
  • Минимальная поддерживаемая версия selenium увеличена с 3.8.0 до 4.8.0.
  • Исключения AlreadyRegistered и NotRegistered перенесены из django.contrib.admin.sites в django.contrib.admin.exceptions.
  • Минимальная поддерживаемая версия SQLite увеличена с 3.21.0 до 3.27.0.
  • Убрана поддержка cx_Oracle < 8.3.
  • Выполнение SQL-запросов до того, как реестр приложений будет полностью заполнен, теперь вызывает ошибку RuntimeWarning.
  • BadRequest будет поднят для запросов с типом содержимого application/x-www-form-urlencoded в кодировке неUTF-8. Более подробную информацию см. в разделе RFC 1866.
  • Минимальная поддерживаемая версия colorama увеличена до 0.4.6.
  • Минимальная поддерживаемая версия docutils увеличена до 0.19.

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

Разное

  • Рендеры переходных форм DjangoDivFormRenderer и Jinja2DivFormRenderer устарели.
  • Передача позиционных аргументов name и violation_error_message в BaseConstraint устарела в пользу аргументов, состоящих только из ключевых слов.
  • request добавляется к сигнатуре ModelAdmin.lookup_allowed(). Поддержка подклассов ModelAdmin, которые не принимают этот аргумент, устарела.
  • Метод get_joining_columns() в ForeignObject и ForeignObjectRel устарел. Начиная с версии Django 6.0, django.db.models.sql.datastructures.Join больше не будет возвращаться к get_joining_columns(). Вместо этого подклассы должны реализовывать get_joining_fields().
  • Метод ForeignObject.get_reverse_joining_columns() устарел.
  • Схема по умолчанию для forms.URLField будет изменена с "http" на "https" в Django 6.0. Установите переходную настройку FORMS_URLFIELD_ASSUME_HTTPS на True, чтобы отказаться от использования "https" во время цикла выпуска Django 5.x.
  • Переходная установка FORMS_URLFIELD_ASSUME_HTTPS устарела.
  • Поддержка вызова format_html() без передачи args или kwargs будет удалена.
  • Поддержка cx_Oracle устарела в пользу драйвера oracledb 1.3.2+ Python.
  • DatabaseOperations.field_cast_sql() устарел в пользу DatabaseOperations.lookup_cast(). Начиная с версии Django 6.0, BuiltinLookup.process_lhs() больше не будет вызывать field_cast_sql(). Сторонние бэкенды баз данных должны реализовывать lookup_cast() вместо этого.
  • Метакласс django.db.models.enums.ChoicesMeta переименован в ChoicesType.
  • Метод Prefetch.get_current_queryset() устарел.
  • Метод get_prefetch_queryset() для связанных менеджеров и дескрипторов устарел. Начиная с версии Django 6.0, get_prefetcher() и prefetch_related_objects() больше не будут возвращаться к get_prefetch_queryset(). Вместо этого подклассы должны реализовывать get_prefetch_querysets().

Функции, удаленные в версии 5.0

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

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

  • Тестовая настройка SERIALIZE удалена.
  • Недокументированный модуль django.utils.baseconv удален.
  • Недокументированный модуль django.utils.datetime_safe удален.
  • Значение по умолчанию для настройки USE_TZ изменяется с False на True.
  • Протокол по умолчанию для карт сайта, созданных вне контекста запроса, изменен с 'http' на 'https'.
  • Аргумент extra_tests для DiscoverRunner.build_suite() и DiscoverRunner.run_tests() удален.
  • Агрегаты django.contrib.postgres.aggregates.ArrayAgg, JSONBAgg и StringAgg больше не возвращают [], [] и '', соответственно, при отсутствии строк.
  • Настройка USE_L10N удаляется.
  • Переходная настройка USE_DEPRECATED_PYTZ удалена.
  • Убрана поддержка часовых поясов pytz.
  • Аргумент is_dst удаляется:
    • QuerySet.datetimes()
    • django.utils.timezone.make_aware()
    • django.db.models.functions.Trunc()
    • django.db.models.functions.TruncSecond()
    • django.db.models.functions.TruncMinute()
    • django.db.models.functions.TruncHour()
    • django.db.models.functions.TruncDay()
    • django.db.models.functions.TruncWeek()
    • django.db.models.functions.TruncMonth()
    • django.db.models.functions.TruncQuarter()
    • django.db.models.functions.TruncYear()
  • Классы django.contrib.gis.admin.GeoModelAdmin и OSMGeoAdmin удалены.
  • Недокументированный метод BaseForm._html_output() удален.
  • Убрана возможность возвращать str, а не SafeString при рендеринге ErrorDict и ErrorList.

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

  • Метод SitemapIndexItem.__str__() удален.
  • Переходная настройка CSRF_COOKIE_MASKED удалена.
  • Аргумент name из django.utils.functional.cached_property() удаляется.
  • Аргумент opclasses из django.contrib.postgres.constraints.ExclusionConstraint удаляется.
  • Убрана недокументированная возможность передачи errors=None в SimpleTestCase.assertFormError() и assertFormsetError().
  • django.contrib.sessions.serializers.PickleSerializer удаляется.
  • Использование QuerySet.iterator() в наборе queryset, который осуществляет предварительную выборку связанных объектов без указания аргумента chunk_size, больше не допускается.
  • Передача несохраненных экземпляров моделей в связанные фильтры больше не допускается.
  • created=True требуется в сигнатуре подклассов RemoteUserBackend.configure_user().
  • Убрана поддержка выхода из системы через запросы GET в django.contrib.auth.views.LogoutView и django.contrib.auth.views.logout_then_login().
  • Псевдоним django.utils.timezone.utc для datetime.timezone.utc удален.
  • Передача объекта ответа и имени формы/набора форм в SimpleTestCase.assertFormError() и assertFormSetError() больше не допускается.
  • Символ django.contrib.gis.admin.OpenLayersWidget удаляется.
  • Символ django.contrib.auth.hashers.CryptPasswordHasher удаляется.
  • Шаблоны "django/forms/default.html" и "django/forms/formsets/default.html" удалены.
  • Стиль отрисовки форм и наборов форм по умолчанию изменен на div-based.
  • Передача nulls_first=False или nulls_last=False в методы Expression.asc() и Expression.desc(), а также выражение OrderBy больше не допускается.
Вернуться на верх