Примечания к выпуску Django 4.2¶
3 апреля 2023 г.
Добро пожаловать в Django 4.2!
Эти заметки о выпуске охватывают new features, а также некоторые backwards incompatible changes, на которые следует обратить внимание при переходе с версии Django 4.1 или более ранней. Мы begun the deprecation process for some features.
См. руководство Как обновить Django до более новой версии, если вы обновляете существующий проект.
Django 4.2 обозначен как long-term support release. Она будет получать обновления безопасности в течение как минимум трех лет после выпуска. Поддержка предыдущего LTS, Django 3.2, завершится в апреле 2024 года.
Совместимость с Python¶
Django 4.2 поддерживает Python 3.8, 3.9, 3.10, 3.11 и 3.12 (начиная с версии 4.2.8). Мы настоятельно рекомендуем и официально поддерживаем только последний релиз каждой серии.
Что нового в Django 4.2¶
Поддержка Psycopg 3¶
Django теперь поддерживает psycopg версии 3.1.8 и выше. Для обновления кода установите psycopg library, ENGINE
менять не нужно, так как django.db.backends.postgresql
поддерживает обе библиотеки.
Поддержка psycopg2
, скорее всего, будет устаревшей и удалена в будущем.
Следует иметь в виду, что в psycopg
3 внесены некоторые принципиальные изменения по сравнению с psycopg2
. В связи с этим может потребоваться внести некоторые изменения для учета differences from psycopg2.
Комментарии к столбцам и таблицам¶
Новые опции Field.db_comment
и Meta.db_table_comment
позволяют создавать комментарии к столбцам и таблицам соответственно. Например:
from django.db import models
class Question(models.Model):
text = models.TextField(db_comment="Poll question")
pub_date = models.DateTimeField(
db_comment="Date and time when the question was published",
)
class Meta:
db_table_comment = "Poll questions"
class Answer(models.Model):
question = models.ForeignKey(
Question,
on_delete=models.CASCADE,
db_comment="Reference to a question",
)
answer = models.TextField(db_comment="Question answer")
class Meta:
db_table_comment = "Question answers"
Кроме того, новая операция AlterModelTableComment
позволяет изменять комментарии к таблице, заданные в Meta.db_table_comment
.
Защита от атаки BREACH¶
GZipMiddleware
теперь включает в себя защиту от атаки BREACH. Оно добавляет до 100 случайных байтов в gzip-ответы, чтобы затруднить атаки BREACH. Подробнее о методе защиты от атак читайте в Heal The Breach (HTB) paper.
Файловое хранилище в памяти¶
Новый класс django.core.files.storage.InMemoryStorage
предоставляет непостоянное хранилище, полезное для ускорения тестирования за счет отказа от обращения к диску.
Пользовательские файловые хранилища¶
Новый параметр STORAGES
позволяет настраивать несколько пользовательских бэкендов хранения файлов. Она также управляет механизмами хранения для управления files (ключ "default"
) и static files (ключ "staticfiles"
).
Старые настройки DEFAULT_FILE_STORAGE
и STATICFILES_STORAGE
в этом выпуске устарели.
Незначительные особенности¶
django.contrib.admin
¶
- Светлая или темная цветовая тема администратора теперь может быть выбрана в пользовательском интерфейсе, а также может быть установлена в соответствии с системными настройками.
- Стек шрифтов администратора теперь предпочитает системные шрифты пользовательского интерфейса и больше не требует загрузки шрифтов. Кроме того, доступны переменные CSS для более удобного переопределения семейств шрифтов по умолчанию.
- Шаблон admin/delete_confirmation.html теперь имеет несколько дополнительных блоков и скриптовых крючков, облегчающих его настройку.
- Выбранные опции виджетов
filter_horizontal
иfilter_vertical
теперь можно фильтровать. - В шаблоне
admin/base.html
теперь появился новый блокnav-breadcrumbs
, содержащий навигационный ориентир и блокbreadcrumbs
. ModelAdmin.list_editable
теперь использует атомарные транзакции при выполнении правок.- jQuery обновлен с версии 3.6.0 до 3.6.4.
django.contrib.auth
¶
- Количество итераций по умолчанию для хешера паролей PBKDF2 увеличено с 390 000 до 600 000.
UserCreationForm
теперь сохраняет поля формы «многие-ко-многим» для пользовательской модели пользователя.- Новый класс
BaseUserCreationForm
теперь является рекомендуемым базовым классом для настройки формы создания пользователя.
django.contrib.gis
¶
- Теперь GeoJSON serializer выводит ключ
id
для сериализованных функций, который по умолчанию равен первичному ключу объектов. - Класс
GDALRaster
теперь поддерживаетpathlib.Path
. - Класс
GeoIP2
теперь поддерживает файлы.mmdb
, загружаемые с DB-IP. - Виджет шаблона OpenLayers больше не включает встроенный CSS (при этом также удаляется прежний блок
map_css
), чтобы лучше соответствовать строгой политике безопасности содержимого. OpenLayersWidget
теперь базируется на OpenLayers 7.2.2 (ранее 4.6.5).- Новый поиск
isempty
и выражениеIsEmpty()
позволяют фильтровать пустые геометрии в PostGIS. - Новые функции
FromWKB()
иFromWKT()
позволяют создавать геометрии из общеизвестного двоичного (WKB) и общеизвестного текстового (WKT) представлений.
django.contrib.postgres
¶
- Новый поиск
trigram_strict_word_similar
, а также выраженияTrigramStrictWordSimilarity()
иTrigramStrictWordDistance()
позволяют использовать триграммное строгое сходство слов. - Поиск
arrayfield.overlap
теперь поддерживаетQuerySet.values()
иvalues_list()
в качестве правой части.
django.contrib.sitemaps
¶
- Новый метод
Sitemap.get_languages_for_item()
позволяет настраивать список языков, для которых отображается элемент.
django.contrib.staticfiles
¶
- В операторе
ManifestStaticFilesStorage
появилась экспериментальная поддержка замены путей к JavaScript-модулям в операторахimport
иexport
на их хэшированные аналоги. Если вы хотите попробовать, создайте подклассManifestStaticFilesStorage
и установите атрибутsupport_js_module_import_aggregation
в значениеTrue
. - Новый атрибут
ManifestStaticFilesStorage.manifest_hash
содержит хэш всех файлов в манифесте и изменяется при изменении одного из файлов.
Бэкенды баз данных¶
- Новая опция
"assume_role"
теперь поддерживается вOPTIONS
на PostgreSQL, позволяя указывать session role. - Новая опция
"server_side_binding"
теперь поддерживается вOPTIONS
на PostgreSQL с версиейpsycopg
3.1.8+, что позволяет использовать server-side binding cursors.
Отчеты об ошибках¶
- На странице отладки теперь отображаются exception notes и fine-grained error locations на Python 3.11+.
- Сессионные cookies теперь рассматриваются как учетные данные и поэтому скрываются, а в отчетах об ошибках заменяются звездочками (
**********
).
Формы¶
ModelForm
теперь принимает новую опциюMeta
formfield_callback
для настройки полей формы.modelform_factory()
теперь уважает атрибутformfield_callback
уform
вMeta
.
Интернационализация¶
- Добавлена поддержка и переводы для центральнокурдского языка (сорани).
Ведение журнала¶
- Регистратор django.db.backends теперь регистрирует запросы управления транзакциями (
BEGIN
,COMMIT
иROLLBACK
) на уровнеDEBUG
.
Команды управления¶
- <<<Команда
makemessages
теперь поддерживает локали с приватными подтегами, такими какnl_NL-x-informal
. - Новая опция
makemigrations --update
объединяет изменения модели в последнюю миграцию и оптимизирует результирующие операции.
Миграции¶
- Миграции теперь поддерживают сериализацию объектов
enum.Flag
.
Модели¶
- Теперь
QuerySet
широко поддерживает фильтрацию по Функции окна, за исключением дизъюнктивного поиска фильтра по оконным функциям при выполнении агрегации. prefetch_related()
теперь поддерживает объектыPrefetch
с нарезанными кверисетами.- Registering lookups на экземплярах
Field
теперь поддерживается. - Новый аргумент
robust
дляon_commit()
позволяет выполнять действия, которые могут завершиться неудачей после успешной фиксации транзакции базы данных. - Новое выражение
KT()
представляет собой текстовое значение ключа, индекса или преобразования путиJSONField
. Now
теперь поддерживает микросекундную точность в MySQL и миллисекундную в SQLite.- Выражения
F()
, выводящиеBooleanField
, теперь можно отрицать с помощью~F()
(оператор инверсии). Model
теперь предоставляет асинхронные версии некоторых методов, использующих базу данных, с использованием префиксаa
:adelete()
,arefresh_from_db()
иasave()
.- Менеджеры Related теперь предоставляют асинхронные версии методов, изменяющих набор связанных объектов, с использованием префикса
a
:aadd()
,aclear()
,aremove()
иaset()
. - <<<Установка
CharField.max_length
больше не требуется в PostgreSQL, который поддерживает неограниченное количество столбцовVARCHAR
.
Запросы и ответы¶
StreamingHttpResponse
теперь поддерживает асинхронные итераторы при обслуживании Django через ASGI.
Тесты¶
Опция
test --debug-sql
теперь форматирует SQL-запросы с помощьюsqlparse
.Классы
RequestFactory
,AsyncRequestFactory
,Client
иAsyncClient
теперь поддерживают параметрheaders
, который принимает словарь имен и значений заголовков. Это позволяет использовать более естественный синтаксис для объявления заголовков.# Before: self.client.get("/home/", HTTP_ACCEPT_LANGUAGE="fr") await self.async_client.get("/home/", ACCEPT_LANGUAGE="fr") # After: self.client.get("/home/", headers={"accept-language": "fr"}) await self.async_client.get("/home/", headers={"accept-language": "fr"})
Утилиты¶
- Новый параметр
encoder
для функцииdjango.utils.html.json_script()
позволяет настраивать класс JSON-кодировщика. - Частная внутренняя вендорная копия
urllib.parse.urlsplit()
теперь зачеркивает'\r'
,'\n'
и'\t'
(см. CVE-2022-0391 и bpo-43882). Это сделано для защиты проектов, которые могут некорректно использовать внутреннюю функциюurl_has_allowed_host_and_scheme()
, вместо того, чтобы использовать одну из документированных функций для работы с URL-перенаправлениями. Функции Django не были затронуты. - Новая функция
django.utils.http.content_disposition_header()
возвращает значение HTTP-заголовкаContent-Disposition
, указанное в RFC 6266.
Валидаторы¶
- Список распространенных паролей, используемых программой
CommonPasswordValidator
, обновляется до последней версии.
Обратные несовместимые изменения в 4.2¶
API бэкенда базы данных¶
В этом разделе описаны изменения, которые могут потребоваться в бэкендах баз данных сторонних производителей.
DatabaseFeatures.allows_group_by_pk
удален, поскольку он оставался только для того, чтобы соответствовать расширению MySQL, которое в MySQL 5.7.15 было вытеснено корректным определением функциональных зависимостей. Отметим, чтоDatabaseFeatures.allows_group_by_selected_pks
по-прежнему поддерживается и должен быть включен, если ваш бэкэнд поддерживает обнаружение функциональных зависимостей в предложенияхGROUP BY
, как это предусмотрено стандартомSQL:1999
.inspectdb
теперь используетdisplay_size
изDatabaseIntrospection.get_table_description()
, а неinternal_size
дляCharField
.
Прекращена поддержка MariaDB 10.3¶
Поддержка MariaDB 10.3 заканчивается в мае 2023 года. Django 4.2 поддерживает MariaDB 10.4 и выше.
Отказано в поддержке MySQL 5.7¶
Поддержка MySQL 5.7 заканчивается в октябре 2023 года. Django 4.2 поддерживает MySQL 8 и выше.
Отказ от поддержки PostgreSQL 11¶
Текущая поддержка PostgreSQL 11 заканчивается в ноябре 2023 года. Django 4.2 поддерживает PostgreSQL 12 и выше.
Теперь может потребоваться установка update_fields
в Model.save()
¶
Чтобы избежать обновления ненужных столбцов, QuerySet.update_or_create()
теперь передает update_fields
в вызовы Model.save()
. Как следствие, любые поля, модифицированные в пользовательских методах save()
, должны быть добавлены в аргумент ключевого слова update_fields
перед вызовом super()
. Более подробная информация приведена в разделе Переопределение методов модели.
Отказались от поддержки сырых агрегатов в MySQL¶
MySQL 8+ разрешает функциональные зависимости от GROUP BY
столбцов, поэтому обходной путь группировки по первичным ключам главной таблицы, существовавший до версии Django 4.2, удален. Как следствие, использование агрегаций RawSQL()
больше не поддерживается в MySQL, так как нет способа определить, нужны ли такие агрегации или допустимы ли они в предложении GROUP BY
. Вместо этого используйте Агрегационные функции.
Разное¶
- Недокументированная функция
django.http.multipartparser.parse_header()
удалена. Вместо нее используйтеdjango.utils.http.parse_header_parameters()
. - Результат
{% blocktranslate asvar … %}
теперь помечен как безопасный для (HTML) вывода. - HTML-атрибут
autofocus
в окне поиска администратора удален, так как он может сбивать с толку пользователей, читающих с экрана. - Опция
makemigrations --check
больше не создает отсутствующие файлы миграции. - Аргумент
alias
дляExpression.get_group_by_cols()
удален. - Минимальная поддерживаемая версия
sqlparse
увеличена с 0.2.2 до 0.3.1. - Недокументированный параметр
negated
выраженияExists
удален. - Аргумент
is_summary
недокументированного методаQuery.add_annotation()
удален. - Минимальная поддерживаемая версия SQLite увеличена с 3.9.0 до 3.21.0.
- Минимальная поддерживаемая версия
asgiref
увеличена с 3.5.2 до 3.6.0. UserCreationForm
теперь отвергает имена пользователей, отличающиеся только регистром. Если вам нужно прежнее поведение, используйте вместо негоBaseUserCreationForm
.- Минимальная поддерживаемая версия
mysqlclient
увеличена с 1.4.0 до 1.4.3. - Минимальная поддерживаемая версия
argon2-cffi
увеличена с 19.1.0 до 19.2.0. - Минимальная поддерживаемая версия
Pillow
увеличена с 6.2.0 до 6.2.1. - Минимальная поддерживаемая версия
jinja2
увеличена с 2.9.2 до 2.11.0. - Минимальная поддерживаемая версия redis-py увеличена с 3.0.0 до 3.4.0.
- Для объектов
WSGIRequest
, инстанцируемых вручную, должен быть предоставлен файлоподобный объектwsgi.input
. Ранее Django был более мягким по сравнению с ожидаемым поведением, указанным в спецификации WSGI. - Устранена поддержка
PROJ
< 5. EmailBackend
теперь проверяет наличиеhostname
иcertificates
. Если вам нужно прежнее поведение, которое является менее ограничительным и не рекомендуется, подклассEmailBackend
и переопределите свойствоssl_context
.
Функции, устаревшие в 4.2¶
<<<Опция index_together
устарела в пользу indexes
.¶
Параметр Meta.index_together
устарел в пользу параметра indexes
.
Миграция существующих index_together
должна обрабатываться как миграция. Например:
class Author(models.Model):
rank = models.IntegerField()
name = models.CharField(max_length=30)
class Meta:
index_together = [["rank", "name"]]
Должно стать:
class Author(models.Model):
rank = models.IntegerField()
name = models.CharField(max_length=30)
class Meta:
indexes = [models.Index(fields=["rank", "name"])]
Выполнение команды makemigrations
приведет к генерации миграции, содержащей операцию RenameIndex
, которая переименует существующий индекс. Далее следует рассмотреть возможность сминания миграций, чтобы удалить index_together
из исторических миграций.
Операция миграции AlterIndexTogether
теперь официально поддерживается только для файлов миграции, созданных до версии Django 4.2. По соображениям обратной совместимости она по-прежнему является частью общедоступного API, и ее отказ от использования или удаление не планируется, но ее не следует использовать для новых миграций. Вместо него используйте операции AddIndex
и RemoveIndex
.
Передача закодированных строковых литералов JSON в JSONField
устарела¶
JSONField
и связанные с ним lookups и aggregates раньше позволяли передавать строковые литералы в кодировке JSON, что вызывало неоднозначность в вопросе о том, закодированы ли уже строковые литералы с точки зрения бэкенда базы данных.
В течение периода обесценивания строковые литералы будут пытаться декодироваться в JSON, а при успехе будет выдаваться предупреждение, указывающее на передачу вместо них некодированных форм.
Код, используемый для передачи строковых литералов в кодировке JSON:
Document.objects.bulk_create(
Document(data=Value("null")),
Document(data=Value("[]")),
Document(data=Value('"foo-bar"')),
)
Document.objects.annotate(
JSONBAgg("field", default=Value("[]")),
)
Должно стать:
Document.objects.bulk_create(
Document(data=Value(None, JSONField())),
Document(data=[]),
Document(data="foo-bar"),
)
Document.objects.annotate(
JSONBAgg("field", default=[]),
)
Начиная с версии Django 5.1+ строковые литералы будут неявно интерпретироваться как строковые литералы JSON.
Разное¶
Метод
BaseUserManager.make_random_password()
является устаревшим. См. recipes and best practices об использовании модуля Pythonsecrets
для генерации паролей.Шаблонный фильтр
length_is
устарел в пользуlength
и оператора==
внутри тега{% if %}
. Например{% if value|length == 4 %}…{% endif %} {% if value|length == 4 %}True{% else %}False{% endif %}
вместо:
{% if value|length_is:4 %}…{% endif %} {{ value|length_is:4 }}
django.contrib.auth.hashers.SHA1PasswordHasher
,django.contrib.auth.hashers.UnsaltedSHA1PasswordHasher
иdjango.contrib.auth.hashers.UnsaltedMD5PasswordHasher
являются устаревшими.django.contrib.postgres.fields.CICharField
устарел в пользуCharField(db_collation="…")
с нечувствительной к регистру недетерминированной сверткой.django.contrib.postgres.fields.CIEmailField
устарел в пользуEmailField(db_collation="…")
с нечувствительной к регистру недетерминированной сверткой.django.contrib.postgres.fields.CITextField
устарел в пользуTextField(db_collation="…")
с нечувствительной к регистру недетерминированной сверткой.Миксин
django.contrib.postgres.fields.CIText
устарел.Атрибуты
map_height
иmap_width
вBaseGeometryWidget
устарели, вместо них используйте CSS для определения размеров виджетов карт.SimpleTestCase.assertFormsetError()
устарел в пользуassertFormSetError()
.TransactionTestCase.assertQuerysetEqual()
устарел в пользуassertQuerySetEqual()
.Передача позиционных аргументов в
Signer
иTimestampSigner
устарела в пользу аргументов, состоящих только из ключевых слов.Настройка
DEFAULT_FILE_STORAGE
устарела в пользуSTORAGES["default"]
.Настройка
STATICFILES_STORAGE
устарела в пользуSTORAGES["staticfiles"]
.Функция
django.core.files.storage.get_storage_class()
является устаревшей.