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

1 декабря 2015 г.

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

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

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

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

Для Django 1.9 требуется Python 2.7, 3.4 или 3.5. Мы настоятельно рекомендуем и официально поддерживаем только последний выпуск каждой серии.

Серия Django 1.8 является последней, поддерживающей Python 3.2 и 3.3.

Что нового в Django 1.9

Выполнение действий после фиксации транзакции

Новый хук on_commit() позволяет выполнять действия после успешной фиксации транзакции базы данных. Это полезно для таких задач, как отправка уведомлений по электронной почте, создание очередей задач или аннулирование кэша.

Эта функциональность из пакета django-transaction-hooks была интегрирована в Django.

Проверка пароля

Django теперь предлагает проверку паролей, чтобы помочь предотвратить использование слабых паролей пользователями. Валидация встроена в прилагаемые формы смены и сброса пароля и легко интегрируется в любой другой код. Проверка выполняется одним или несколькими валидаторами, настраиваемыми в новой настройке AUTH_PASSWORD_VALIDATORS.

В Django включены четыре валидатора, которые могут обеспечивать минимальную длину, сравнивать пароль с атрибутами пользователя, например, с его именем, проверять, чтобы пароль не был полностью числовым, или сверяться со списком распространенных паролей. Вы можете комбинировать несколько валидаторов, а некоторые валидаторы имеют собственные параметры настройки. Например, вы можете предоставить пользовательский список общих паролей. Каждый валидатор содержит текст справки, объясняющий пользователю его требования.

По умолчанию валидация не производится, и все пароли принимаются, поэтому если вы не установите AUTH_PASSWORD_VALIDATORS, вы не увидите никаких изменений. В новых проектах, созданных с шаблоном по умолчанию startproject, включается простой набор валидаторов. Чтобы включить базовую валидацию во включенных auth-формах для вашего проекта, вы можете установить, например:

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]

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

Миксины разрешений для представлений на основе классов

Django теперь поставляется с миксинами AccessMixin, LoginRequiredMixin, PermissionRequiredMixin и UserPassesTestMixin для обеспечения функциональности django.contrib.auth.decorators для представлений на основе классов. Эти миксины были взяты из проекта <<<5 >>> или, по крайней мере, вдохновлены им.

Однако есть несколько различий между реализацией Django и django-braces':

  • Атрибут raise_exception может быть только True или False. Пользовательские исключения или callables не поддерживаются.
  • Метод handle_no_permission() не принимает аргумент request. Текущий запрос доступен в self.request.
  • Пользовательский test_func() из UserPassesTestMixin не принимает аргумент user. Текущий пользователь доступен в self.request.user.
  • Атрибут permission_required поддерживает строку (определяющую одно разрешение) или список/кортеж строк (определяющих несколько разрешений), которые должны быть выполнены для предоставления доступа.
  • Новый атрибут permission_denied_message позволяет передать сообщение в исключение PermissionDenied.

Новая стилизация для contrib.admin

Админка имеет современный плоский дизайн с новыми SVG иконками, которые идеально смотрятся на экранах HiDPI. Он по-прежнему обеспечивает полноценную работу в браузерах YUI’s A-grade. В более старых браузерах может наблюдаться различный уровень плавной деградации.

Параллельное выполнение тестов

Команда test теперь поддерживает опцию --parallel для параллельного запуска тестов проекта в нескольких процессах.

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

Эта опция включена по умолчанию для собственного набора тестов, предоставляемого Django:

  • ОС поддерживает его (все, кроме Windows)
  • бэкенд базы данных поддерживает его (все встроенные бэкенды, кроме Oracle)

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

django.contrib.admin

  • Представления администратора теперь имеют атрибуты model_admin или admin_site.
  • URL вида изменения администратора был изменен (по умолчанию был /admin/<app>/<model>/<pk>/, а теперь /admin/<app>/<model>/<pk>/change/). Это не должно повлиять на ваше приложение, если только у вас нет жестко закодированных URL администратора. В этом случае замените эти ссылки на reversing admin URLs. Обратите внимание, что старый URL по-прежнему перенаправляет на новый для обратной совместимости, но он может быть удален в будущей версии.
  • ModelAdmin.get_list_select_related() был добавлен, чтобы позволить изменять значения select_related(), используемые в запросе списка изменений администратора, в зависимости от запроса.
  • В метод available_apps добавлена контекстная переменная AdminSite.each_context(), которая перечисляет доступные приложения для текущего пользователя.
  • AdminSite.empty_value_display и ModelAdmin.empty_value_display были добавлены для переопределения отображения пустых значений в списке изменений администратора. Вы также можете настроить значение для каждого поля.
  • Добавлены события jQuery when an inline form is added or removed на странице формы изменения.
  • Виджет выбора времени включает опцию «6 часов вечера» для единообразия, чтобы иметь предопределенные опции каждые 6 часов.
  • Генерация JavaScript slug теперь поддерживает румынские символы.

django.contrib.admindocs

  • Раздел модели admindocs теперь также описывает методы, принимающие аргументы, а не игнорирует их.

django.contrib.auth

  • Количество итераций по умолчанию для хешера паролей PBKDF2 было увеличено на 20%. Это обратно совместимое изменение не повлияет на пользователей, которые подклассифицировали django.contrib.auth.hashers.PBKDF2PasswordHasher для изменения значения по умолчанию.
  • Теперь BCryptSHA256PasswordHasher будет обновлять пароли, если изменен его атрибут rounds.
  • AbstractBaseUser и BaseUserManager были перемещены в новый модуль django.contrib.auth.base_user, чтобы их можно было импортировать без включения django.contrib.auth в INSTALLED_APPS (это вызывало предупреждение об устаревании в старых версиях и больше не поддерживается в Django 1.9).
  • Аргумент разрешения permission_required() принимает все виды итераций, а не только список и кортежи.
  • Новый PersistentRemoteUserMiddleware позволяет использовать REMOTE_USER для настроек, в которых заголовок заполняется только на страницах входа в систему, а не при каждом запросе в сессии.
  • Представление django.contrib.auth.views.password_reset() принимает параметр extra_email_context.

django.contrib.contenttypes

django.contrib.gis

  • Все методы GeoQuerySet были устаревшими и заменены на equivalent database functions. Как только устаревшие методы будут заменены в вашем коде, вы даже сможете удалить специальные GeoManager из ваших классов с поддержкой GIS.
  • Интерфейс GDAL теперь поддерживает инстанцирование на основе файлов и в памяти GDALRaster objects из необработанных данных. Добавлены задатчики для свойств растра, таких как проекция или значения пикселей.
  • Для пользователей PostGIS новый RasterField позволяет storing GDALRaster objects. Он поддерживает автоматическое создание пространственного индекса и репроецирование при сохранении модели. Он пока не поддерживает пространственные запросы.
  • Новый метод GDALRaster.warp() позволяет деформировать растр, указывая целевые свойства растра, такие как начало координат, ширина, высота или размер пикселя (среди прочих).
  • Новый метод GDALRaster.transform() позволяет преобразовать растр в другую пространственную систему отсчета, указав цель srid.
  • Новый класс GeoIP2 позволяет использовать базы данных MaxMind’s GeoLite2, которые включают поддержку адресов IPv6.
  • Версия библиотеки OpenLayers, по умолчанию включаемая в виджеты, была обновлена с 2.13 до 2.13.1.

django.contrib.postgres

django.contrib.sessions

django.contrib.sites

  • get_current_site() теперь обрабатывает случай, когда request.get_host() возвращает domain:port, например, example.com:80. Если поиск не удался, потому что хост не соответствует записи в базе данных, а у хоста есть порт, порт удаляется и поиск повторяется только с доменной частью.

django.contrib.syndication

  • Добавлена поддержка нескольких корпусов для каждого элемента ленты. Если в RSS-канале определено несколько корпусов, то возникает исключение, так как RSS-каналы, в отличие от Atom-каналов, не поддерживают несколько корпусов на один элемент ленты.

Кэш

  • django.core.cache.backends.base.BaseCache теперь имеет метод get_or_set().
  • django.views.decorators.cache.never_cache() теперь посылает более убедительные заголовки (добавлены no-cache, no-store, must-revalidate к Cache-Control), чтобы лучше предотвратить кэширование. Это также было добавлено в Django 1.8.8.

CSRF

  • Имя заголовка запроса, используемое для аутентификации CSRF, может быть настроено с помощью CSRF_HEADER_NAME.
  • Заголовок CSRF referer теперь проверяется на соответствие параметру CSRF_COOKIE_DOMAIN, если он установлен. Подробнее смотрите Как это работает.
  • Новая настройка CSRF_TRUSTED_ORIGINS предоставляет возможность разрешить кросс-оригинальные небезопасные запросы (например, POST) через HTTPS.

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

  • Бэкенд PostgreSQL (django.db.backends.postgresql_psycopg2) также доступен как django.db.backends.postgresql. Старое название будет по-прежнему доступно для обратной совместимости.

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

  • Storage.get_valid_name() теперь вызывается, когда upload_to является вызываемым.
  • File теперь имеет метод seekable() при использовании Python 3.

Формы

  • ModelForm принимает новую опцию Meta field_classes для настройки типа полей. Подробности см. в Переопределение полей по умолчанию.
  • Теперь вы можете указать порядок отображения полей формы с помощью атрибута field_order, аргумента конструктора field_order или метода order_fields().
  • Префикс формы может быть указан внутри класса формы, а не только при инстанцировании формы. Подробнее см. в разделе Префиксы для форм.
  • Теперь вы можете specify keyword arguments, которые вы хотите передать в конструктор форм в наборе форм.
  • SlugField теперь принимает аргумент allow_unicode, чтобы разрешить использование символов Unicode в строках.
  • CharField теперь принимает аргумент strip, чтобы очистить входные данные от пробелов, идущих впереди и позади. Поскольку по умолчанию это значение равно True, это поведение отличается от предыдущих версий.
  • Поля формы теперь поддерживают аргумент disabled, что позволяет браузерам отключать отображение виджета поля.
  • Теперь можно настраивать связанные поля, переопределяя метод get_bound_field() поля.

Общие представления

  • Представления на основе классов, созданные с помощью as_view(), теперь имеют атрибуты view_class и view_initkwargs.
  • method_decorator() теперь можно использовать со списком или кортежем декораторов. Он также может быть использован для decorate classes instead of methods.

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

  • Представление django.views.i18n.set_language() теперь правильно перенаправляет на translated URLs, когда оно доступно.
  • Представление django.views.i18n.javascript_catalog() теперь работает правильно при многократном использовании с разными конфигурациями на одной странице.
  • Функция django.utils.timezone.make_aware() обзавелась аргументом is_dst, чтобы помочь решить проблему неоднозначного времени при переходе на летнее время.
  • Теперь вы можете использовать варианты локали, поддерживаемые gettext. Обычно они используются для языков, которые могут быть написаны разными шрифтами, например, латиницей и кириллицей (например, be@latin).
  • Добавлено представление django.views.i18n.json_catalog() для помощи в создании пользовательской клиентской библиотеки i18n на основе переводов Django. Оно возвращает объект JSON, содержащий каталог переводов, настройки форматирования и правило множественного числа.
  • Добавлен атрибут name_translated к объекту, возвращаемому тегом шаблона get_language_info. Также добавлен соответствующий фильтр шаблона: language_name_translated.
  • Теперь вы можете запустить compilemessages из корневого каталога вашего проекта, и он найдет все файлы сообщений приложения, которые были созданы makemessages.
  • makemessages теперь вызывает xgettext один раз на каталог локали, а не на каждый переводимый файл. Это ускоряет сборку локализации.
  • blocktrans поддерживает присвоение его выхода переменной с помощью asvar.
  • Доступны два новых языка: колумбийский испанский и шотландский гэльский.

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

  • Новая команда sendtestemail позволяет отправить тестовое письмо, чтобы легко подтвердить, что отправка писем через Django работает.
  • Для повышения удобочитаемости SQL-кода, генерируемого sqlmigrate, SQL-код, генерируемый для каждой операции миграции, предваряется описанием операции.
  • Вывод команды dumpdata теперь детерминированно упорядочен. Более того, когда указана опция --output, она также показывает в терминале индикатор выполнения.
  • Команда createcachetable теперь имеет флаг --dry-run, чтобы распечатать SQL, а не выполнять его.
  • Команда startapp создает файл apps.py. Поскольку она не использует default_app_config (a discouraged API), вы должны указать путь к конфигурации приложения, например 'polls.apps.PollsConfig', в INSTALLED_APPS, чтобы он был использован (а не просто 'polls').
  • При использовании бэкенда PostgreSQL команда dbshell может подключаться к базе данных, используя пароль из вашего файла настроек (вместо того, чтобы требовать его ввода вручную).
  • Пакет django может быть запущен как сценарий, т.е. python -m django, который будет вести себя так же, как django-admin.
  • Команды управления, имеющие опцию --noinput, теперь также принимают --no-input в качестве псевдонима для этой опции.

Миграции

  • Начальные миграции теперь помечаются атрибутом класса initial = True, что позволяет migrate --fake-initial легче обнаруживать начальные миграции.

  • Добавлена поддержка сериализации экземпляров functools.partial и LazyObject.

  • При указании None в качестве значения в MIGRATION_MODULES, Django будет считать приложение приложением без миграций.

  • При применении миграций шаг «Rendering model states», который отображается при запуске migrate с verbosity 2 или выше, теперь вычисляет только состояния для миграций, которые уже были применены. Состояния модели для применяемых миграций генерируются по требованию, что значительно сокращает объем требуемой памяти.

    Однако это улучшение недоступно при неприменении миграций и поэтому требует предварительного вычисления и хранения промежуточных состояний миграции.

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

  • Команда squashmigrations теперь поддерживает указание начальной миграции, с которой будут сминаться миграции.

Модели

  • QuerySet.bulk_create() теперь работает на прокси-моделях.
  • В конфигурации баз данных появилась опция TIME_ZONE для взаимодействия с базами данных, хранящими даты в местном времени и не поддерживающими часовые пояса, когда USE_TZ становится True.
  • Добавлен метод RelatedManager.set() к связанным менеджерам, созданным ForeignKey, GenericForeignKey и ManyToManyField.
  • Метод add() на обратном внешнем ключе теперь имеет параметр bulk, что позволяет выполнять один запрос независимо от количества добавляемых объектов, а не один запрос на объект.
  • Добавлен параметр keep_parents к Model.delete() для возможности удаления только дочерних данных в модели, использующей многотабличное наследование.
  • Model.delete() и QuerySet.delete() возвращают количество удаленных объектов.
  • Добавлена системная проверка для предотвращения определения Meta.ordering и order_with_respect_to в одной модели.
  • Поиск Date and time может быть связан с другими поисками (такими как exact, gt, lt и т.д.). Например: Entry.objects.filter(pub_date__month__gt=6).
  • Поиск времени (час, минута, секунда) теперь поддерживается TimeField для всех бэкендов баз данных. Поддержка бэкендов, отличных от SQLite, была добавлена, но недокументирована в Django 1.7.
  • Вы можете указать параметр output_field агрегата Avg для агрегации по нечисловым столбцам, например DurationField.
  • Добавлен поиск date в DateTimeField, чтобы можно было запрашивать поле только по части даты.
  • Добавлены функции базы данных Greatest и Least.
  • Добавлена функция базы данных Now, которая возвращает текущую дату и время.
  • Transform теперь является подклассом Func(), который позволяет использовать Transforms в правой части выражения, как обычные Funcs. Это позволяет регистрировать некоторые функции базы данных, такие как Length, Lower и Upper как преобразования.
  • SlugField теперь принимает аргумент allow_unicode, чтобы разрешить использование символов Unicode в строках.
  • Добавлена поддержка ссылок на аннотации в QuerySet.distinct().
  • connection.queries показывает запросы с подставленными параметрами на SQLite.
  • Query expressions теперь можно использовать при создании новых экземпляров модели с помощью save(), create() и bulk_create().

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

  • Если HttpResponse.reason_phrase не задан явно, то теперь он определяется текущим значением HttpResponse.status_code. Изменение значения status_code вне конструктора также изменит значение reason_phrase.
  • В представлении отладки теперь отображаются подробности цепочки исключений на Python 3.
  • Представления ошибок 40x по умолчанию теперь принимают второй позиционный параметр - исключение, которое вызвало представление.
  • Обработчики ошибок представления теперь поддерживают TemplateResponse, обычно используемые в представлениях, основанных на классах.
  • Исключения, вызванные методом render(), теперь передаются в метод process_exception() каждого промежуточного ПО.
  • Теперь промежуточное ПО запроса может установить HttpRequest.urlconf в None, чтобы отменить все изменения, сделанные предыдущим промежуточным ПО, и вернуться к использованию ROOT_URLCONF.
  • Проверка DISALLOWED_USER_AGENTS в CommonMiddleware теперь вызывает исключение PermissionDenied, а не возвращает HttpResponseForbidden, так что вызывается handler403.
  • Добавлено HttpRequest.get_port() для получения порта отправителя запроса.
  • Добавлен параметр json_dumps_params в JsonResponse, позволяющий передавать аргументы ключевого слова в вызов json.dumps(), используемый для генерации ответа.
  • Теперь BrokenLinkEmailsMiddleware игнорирует 404, когда referer равен запрашиваемому URL. Чтобы обойти уже реализованную проверку пустого реферера, некоторые веб-боты устанавливают реферер равным запрашиваемому URL.

Шаблоны

  • Теги шаблонов, созданные с помощью помощника simple_tag(), теперь могут хранить результаты в переменной шаблона, используя аргумент as.
  • Добавлен метод Context.setdefault().
  • Был добавлен регистратор django.template, который включает следующие сообщения:
    • Сообщение уровня DEBUG для отсутствующих контекстных переменных.
    • Сообщение уровня WARNING для не пойманных исключений, поднятых во время рендеринга {% include %}, когда режим отладки выключен (полезно, поскольку {% include %} глушит исключение и возвращает пустую строку).
  • Тег шаблона firstof поддерживает сохранение вывода в переменной с помощью „as“.
  • Context.update() теперь можно использовать в качестве менеджера контекста.
  • Загрузчики шаблонов Django теперь могут расширять шаблоны рекурсивно.
  • Шаблон отладочной страницы postmortem теперь включает вывод от каждого установленного движка.
  • Debug page integration для пользовательских шаблонизаторов был добавлен.
  • Бэкенд DjangoTemplates получил возможность явной регистрации библиотек и встроенных модулей через шаблон OPTIONS.
  • Фильтры timesince и timeuntil были улучшены для работы с високосными годами при больших временных интервалах.
  • Тег include теперь кэширует разобранные объекты шаблонов во время рендеринга шаблона, что ускоряет повторное использование в таких местах, как циклы for.

Тесты

  • Добавлен метод json() для тестирования ответов клиента, чтобы дать доступ к телу ответа в виде JSON.
  • В тестовый клиент добавлен метод force_login(). Используйте этот метод для имитации эффекта, когда пользователь входит на сайт, пропуская этапы аутентификации и верификации login().

URL-адреса

  • Утверждения обхода регулярных выражений теперь разрешены в шаблонах URL.
  • Пространство имен приложения теперь можно задать с помощью атрибута app_name на включенном модуле или объекте. Его также можно задать, передав кортеж (<список шаблонов>, <пространство имен приложений>) в качестве первого аргумента в include().
  • Добавлены системные проверки на распространенные ошибки в шаблонах URL.

Валидаторы

  • Добавлено django.core.validators.int_list_validator() для генерации валидаторов строк, содержащих целые числа, разделенные пользовательским символом.
  • EmailValidator теперь ограничивает длину меток доменных имен до 63 символов на RFC 1034.
  • Добавлено validate_unicode_slug() для проверки строк, которые могут содержать символы Unicode.

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

Предупреждение

В дополнение к изменениям, описанным в этом разделе, обязательно просмотрите Функции, удаленные в 1.9 для функций, которые достигли конца своего цикла устаревания и поэтому были удалены. Если вы не обновили свой код в течение срока устаревания данной функции, ее удаление может выглядеть как изменение, несовместимое с обратным ходом развития.

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

  • Пара новых тестов полагается на способность бэкенда интроспектировать значения столбцов по умолчанию (возвращая результат в виде Field.default). Вы можете установить функцию базы данных can_introspect_default на False, если ваш бэкенд этого не делает. Вы можете просмотреть реализацию на бэкендах, которые Django включает для справки (#24245).

  • Не рекомендуется регистрировать глобальный адаптер или конвертер на уровне модуля DB-API для обработки информации о часовом поясе значений datetime, передаваемых в качестве параметров запроса или возвращаемых в качестве результатов запроса в базах данных, не поддерживающих часовые пояса. Это может привести к конфликту с другими библиотеками.

    Рекомендуемый способ добавления часового пояса к значениям datetime, получаемым из базы данных, заключается в регистрации конвертера для DateTimeField в DatabaseOperations.get_db_converters().

    Функция базы данных needs_datetime_string_cast была удалена. Бэкенды баз данных, которые его устанавливают, должны зарегистрировать вместо него конвертер, как объяснено выше.

  • Методы DatabaseOperations.value_to_db_<type>() были переименованы в adapt_<type>field_value(), чтобы отразить методы convert_<type>field_value().

  • Для использования нового поиска date сторонним бэкендам баз данных может потребоваться реализовать метод DatabaseOperations.datetime_cast_date_sql().

  • Был добавлен метод DatabaseOperations.time_extract_sql(). Он вызывает существующий метод date_extract_sql(). Этот метод переопределяется бэкендом SQLite для добавления поиска времени (час, минута, секунда) в TimeField, а также может понадобиться сторонним бэкендам баз данных.

  • Метод DatabaseOperations.datetime_cast_sql() (не путать с DatabaseOperations.datetime_cast_date_sql(), упомянутым выше) был удален. Этот метод служил для форматирования дат в Oracle задолго до 1.0, но он не был переопределен ни одним бэкендом ядра в течение многих лет и не вызывался нигде в коде или тестах Django.

  • Для поддержки распараллеливания тестов вы должны реализовать метод DatabaseCreation._clone_test_db() и установить DatabaseFeatures.can_clone_databases = True. Возможно, вам придется настроить DatabaseCreation.get_test_db_clone_settings().

Параметры по умолчанию, которые были кортежами, теперь стали списками

Настройки по умолчанию в django.conf.global_settings представляли собой комбинацию списков и кортежей. Все настройки, которые раньше были кортежами, теперь стали списками.

Атрибут is_usable на загрузчиках шаблонов удален

Загрузчики шаблонов Django ранее требовали определения атрибута is_usable. Если загрузчик был настроен в настройках шаблона и этот атрибут был False, то загрузчик молча игнорировался. На практике это использовалось только загрузчиком яиц, чтобы определить, установлен ли setuptools. Теперь атрибут is_usable удален, и вместо этого загрузчик яиц не срабатывает во время выполнения, если setuptools не установлен.

Загрузчики шаблонов на базе файловой системы отлавливают более специфические исключения

При использовании загрузчиков шаблонов filesystem.Loader или app_directories.Loader ранние версии Django выдавали ошибку TemplateDoesNotExist, если источник шаблона существовал, но был нечитаемым. Это могло произойти при многих обстоятельствах, например, если у Django не было прав на открытие файла, или если источником шаблона была директория. Теперь Django замалчивает исключение только в том случае, если источник шаблона не существует. Во всех остальных ситуациях возникает исходный сигнал IOError.

HTTP-перенаправления больше не принудительно направляются на абсолютные URI

Относительные перенаправления больше не преобразуются в абсолютные URI. RFC 2616 требовал, чтобы заголовок Location в ответах перенаправления был абсолютным URI, но он был заменен RFC 7231, который позволяет относительные URI в Location, признавая фактическую практику пользовательских агентов, почти все из которых поддерживают их.

Следовательно, ожидаемые URL, передаваемые в assertRedirects, как правило, больше не должны включать схему и доменную часть URL. Например, self.assertRedirects(response, 'http://testserver/some-url/') должно быть заменено на self.assertRedirects(response, '/some-url/') (если только перенаправление не содержало абсолютный URL).

В редких случаях, когда вам нужно старое поведение (обнаруженное в древней версии Apache с mod_scgi, которая интерпретирует относительный редирект как «внутренний редирект»), вы можете восстановить его, написав пользовательское промежуточное ПО:

class LocationHeaderFix(object):
    def process_response(self, request, response):
        if 'Location' in response:
            response['Location'] = request.build_absolute_uri(response['Location'])
        return response

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

Поддержка PostgreSQL 9.0 завершилась в сентябре 2015 года. Как следствие, Django 1.9 устанавливает 9.1 в качестве минимальной версии PostgreSQL, которую он официально поддерживает.

Прекращена поддержка Oracle 11.1

Поддержка Oracle 11.1 завершилась в августе 2015 года. Как следствие, Django 1.9 устанавливает 11.2 в качестве минимальной версии Oracle, которую он официально поддерживает.

Шаблон LoaderOrigin и StringOrigin удалены

В предыдущих версиях Django, когда шаблонизатор инициализировался с отладкой True, в качестве атрибута origin объекта шаблона устанавливался экземпляр django.template.loader.LoaderOrigin или django.template.base.StringOrigin. Эти классы были объединены в Origin и теперь всегда устанавливаются независимо от настройки отладки движка. Для минимального уровня обратной совместимости старые имена классов будут сохранены в качестве псевдонимов для нового класса Origin до выхода Django 2.0.

Изменения в конфигурации протоколирования по умолчанию

Чтобы облегчить написание пользовательских конфигураций логирования, стандартная конфигурация логирования Django больше не определяет django.request и django.security регистраторы. Вместо этого, она определяет один регистратор django, отфильтрованный на уровне INFO, с двумя обработчиками:

  • console: фильтруется на уровне INFO и активен только при DEBUG=True.
  • mail_admins: фильтруется на уровне ERROR и активен только при DEBUG=False.

Если вы не переопределяете логирование Django по умолчанию, вы должны увидеть минимальные изменения в поведении, но вы можете увидеть некоторые новые логи в консоли runserver, например.

Если вы переопределяете логирование по умолчанию в Django, вам следует проверить, как ваша конфигурация сочетается с новыми настройками по умолчанию.

HttpRequest подробности в отчете об ошибках

Было излишним отображать полную информацию о HttpRequest каждый раз, когда она появлялась в качестве переменной стекового кадра в HTML-версии отладочной страницы и письма об ошибке. Таким образом, теперь в HTTP-запросе будет отображаться то же стандартное представление, что и для других переменных (repr(request)). В результате, метод ExceptionReporterFilter.get_request_repr() и недокументированная функция django.http.build_request_repr() были удалены.

Содержимое текстовой версии письма было изменено, чтобы обеспечить отслеживание той же структуры, что и в случае AJAX-запросов. Детали трассировки выводятся методом ExceptionReporter.get_traceback_text().

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

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

  • Настройкой USE_TZ является True.
  • База данных - SQLite, MySQL, Oracle или база данных стороннего производителя, которая не поддерживает часовые пояса. В случае сомнений можно проверить значение connection.features.supports_timezones.
  • Код запрашивает базу данных вне ORM, обычно с помощью cursor.execute(sql, params).

Если вы передаете в такие запросы знающие параметры datetime, вам следует преобразовать их в наивные даты в UTC:

from django.utils import timezone
param = timezone.make_naive(param, timezone.utc)

Если вы этого не сделаете, преобразование будет выполнено как в более ранних версиях (с предупреждением об устаревании) вплоть до Django 1.11. Django 2.0 не будет выполнять никакого преобразования, что может привести к повреждению данных.

Если вы считываете из результатов значения datetime, они будут наивными, а не осознанными. Вы можете компенсировать это следующим образом:

from django.utils import timezone
value = timezone.make_aware(value, timezone.utc)

Вам не нужно ничего из этого, если вы запрашиваете базу данных через ORM, даже если вы используете запросы raw(). ORM позаботится об управлении информацией о часовом поясе.

Модули тегов шаблонов импортируются при настройке шаблонов

Бэкенд DjangoTemplates теперь выполняет обнаружение установленных модулей тегов шаблонов при инстанцировании. Это обновление позволяет явно предоставлять библиотеки через ключ 'libraries' из OPTIONS при определении бэкенда DjangoTemplates. Ошибки импорта или синтаксиса в модулях тегов шаблонов теперь срабатывают раньше во время инстанцирования, а не при первой компиляции шаблона с тегом {% load %}.

django.template.base.add_to_builtins() удаляется

Хотя это был частный API, проекты обычно использовали add_to_builtins(), чтобы сделать теги шаблонов и фильтры доступными без использования тега {% load %}. Этот API был формализован. Теперь проекты должны определять встроенные библиотеки через ключ 'builtins' в OPTIONS при определении бэкенда DjangoTemplates.

simple_tag теперь обертывает вывод тега в conditional_escape

В общем случае теги шаблонов не автоскейпят свое содержимое, и это поведение documented. Для таких тегов, как inclusion_tag, это не проблема, так как включенный шаблон будет выполнять автоэскейп. Для assignment_tag() вывод будет экранирован, когда он будет использоваться как переменная в шаблоне.

Однако для предполагаемых случаев использования simple_tag очень легко получить неправильный HTML и, возможно, XSS-эксплойт. Например:

@register.simple_tag(takes_context=True)
def greeting(context):
    return "Hello {0}!".format(context['request'].user.first_name)

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

В Django 1.9 это исправлено: если в контексте шаблона установлено autoescape=True (по умолчанию), то simple_tag будет обертывать вывод функции тега с conditional_escape().

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

  • Любой код, генерирующий HTML, должен использовать либо систему шаблонов, либо format_html().
  • Если вывод simple_tag требует экранирования, используйте escape() или conditional_escape().
  • Если вы абсолютно уверены, что выводите HTML из доверенного источника (например, из поля CMS, которое хранит HTML, введенный администраторами), вы можете пометить его как таковой, используя mark_safe().

Теги, которые следуют этим правилам, будут корректны и безопасны независимо от того, выполняются ли они на Django 1.9+ или более ранних версиях.

Paginator.page_range

Paginator.page_range теперь является итератором, а не списком.

В версиях Django, предшествующих 1.8, Paginator.page_range возвращало list в Python 2 и range в Python 3. Django 1.8 постоянно возвращает список, но итератор более эффективен.

Существующий код, который зависит от специфических возможностей list, таких как индексирование, может быть перенесен путем преобразования итератора в list с помощью list().

Неявный поиск QuerySet __in удален

В более ранних версиях такие запросы, как:

Model.objects.filter(related_id=RelatedModel.objects.all())

неявно преобразуется в:

Model.objects.filter(related_id__in=RelatedModel.objects.all())

в результате чего получается SQL типа "related_id IN (SELECT id FROM ...)".

Этого неявного __in больше не происходит, поэтому «IN» SQL теперь «=», и если подзапрос возвращает несколько результатов, по крайней мере, некоторые базы данных будут выдавать ошибку.

contrib.admin поддержка браузера

Администратор больше не поддерживает Internet Explorer 8 и ниже, так как срок службы этих браузеров истек.

CSS и изображения для поддержки Internet Explorer 6 и 7 были удалены. Значки PNG и GIF были заменены значками SVG, которые не поддерживаются Internet Explorer 8 и более ранними версиями.

Библиотека jQuery, встроенная в админку, была обновлена с версии 1.11.2 до 2.1.4. jQuery 2.x имеет тот же API, что и jQuery 1.x, но не поддерживает Internet Explorer 6, 7 или 8, что позволяет повысить производительность и уменьшить размер файла. Если вам нужна поддержка IE8, а также необходимо использовать последнюю версию Django, вы можете заменить администраторскую копию jQuery на свою собственную, создав Django-приложение с такой структурой:

app/static/admin/js/vendor/
    jquery.js
    jquery.min.js

SyntaxError при установке Django setuptools 5.5.x

При установке Django 1.9 или 1.9.1 с помощью setuptools 5.5.x вы увидите:

Compiling django/conf/app_template/apps.py ...
  File "django/conf/app_template/apps.py", line 4
    class {{ camel_case_app_name }}Config(AppConfig):
          ^
SyntaxError: invalid syntax

Compiling django/conf/app_template/models.py ...
  File "django/conf/app_template/models.py", line 1
    {{ unicode_literals }}from django.db import models
                             ^
SyntaxError: invalid syntax

Можно игнорировать эти ошибки (Django все равно установится), но вы можете избежать их, обновив setuptools до более свежей версии. Если вы используете pip, вы можете обновить pip с помощью команды python -m pip install -U pip, которая также обновит setuptools. Эта проблема решена в более поздних версиях Django, как описано в Примечания к выпуску Django 1.9.2.

Разное

  • Статические файлы jQuery в contrib.admin были перемещены в подкаталог vendor/jquery.
  • Текст, отображаемый для нулевых столбцов в ячейках списка изменений администратора list_display, изменился с (None) (или его переведенного эквивалента) на - (тире).
  • django.http.responses.REASON_PHRASES и django.core.handlers.wsgi.STATUS_CODE_TEXT были удалены. Вместо них используйте stdlib Python: http.client.responses для Python 3 и httplib.responses для Python 2.
  • ValuesQuerySet и ValuesListQuerySet были удалены.
  • Шаблон admin/base.html больше не устанавливает window.__admin_media_prefix__ или window.__admin_utc_offset__. Ссылки на изображения в JavaScript, которые использовали это значение для построения абсолютных URL, были перенесены в CSS для упрощения настройки. Смещение UTC хранится в атрибуте data тега <body>.
  • Валидация CommaSeparatedIntegerField была усовершенствована, чтобы запретить такие значения, как ',', ',1' и '1,,2'.
  • Инициализация формы была перенесена из метода ProcessFormView.get() в новый метод FormMixin.get_context_data(). Это может быть обратно несовместимо, если вы переопределили метод get_context_data() без вызова super().
  • Поддержка PostGIS 1.5 была прекращена.
  • Поле django.contrib.sites.models.Site.domain было изменено на unique.
  • Чтобы обеспечить изоляцию тестов, запросы к базе данных по умолчанию больше не разрешены в тестах SimpleTestCase. Вы можете отключить это поведение, установив атрибут allow_database_queries class в True на вашем тестовом классе.
  • ResolverMatch.app_name был изменен, чтобы содержать полный путь к пространству имен в случае вложенных пространств имен. Для согласованности с ResolverMatch.namespace пустым значением теперь является пустая строка вместо None.
  • Для усиления безопасности сеансовые ключи должны состоять не менее чем из 8 символов.
  • Частная функция django.utils.functional.total_ordering() была удалена. Она содержала обходное решение для ошибки functools.total_ordering() в версиях Python старше 2.7.3.
  • Сериализация XML (либо через dumpdata, либо через фреймворк синдикации) раньше выводила любые полученные символы. Теперь, если сериализуемый контент содержит какие-либо управляющие символы, не разрешенные стандартом XML 1.0, сериализация завершится неудачей с ошибкой ValueError.
  • CharField теперь по умолчанию удаляет из вводимого текста ведущие и завершающие пробельные символы. Это можно отключить, установив новый аргумент strip в значение False.
  • Текст шаблона, который переводится и использует два или более последовательных знаков процента, например "%%", может иметь новый msgid после выполнения makemessages (скорее всего, перевод будет помечен как нечеткий). Новый msgid будет помечен как "#, python-format".
  • Если ни request.current_app, ни Context.current_app не установлены, тег шаблона url теперь будет использовать пространство имен текущего запроса. Установите request.current_app в None, если вы не хотите использовать подсказку пространства имен.
  • Настройка SILENCED_SYSTEM_CHECKS теперь глушит сообщения всех уровней. Ранее сообщения уровня ERROR и выше выводились на консоль.
  • Поле FlatPage.enable_comments удаляется из FlatPageAdmin, поскольку оно не используется приложением. Если ваш проект или стороннее приложение использует его, create a custom ModelAdmin добавьте его обратно.
  • Возвращаемое значение setup_databases() и первый аргумент teardown_databases() изменились. Раньше они были кортежами (old_names, mirrors). Теперь это только первый элемент, old_names.
  • По умолчанию LiveServerTestCase пытается найти доступный порт в диапазоне 8081-8179 вместо того, чтобы просто попробовать порт 8081.
  • Системные проверки для ModelAdmin теперь проверяют экземпляры, а не классы.
  • Частный API для применения смешанных планов миграции был упразднен по соображениям производительности. Смешанные планы состоят из списка миграций, некоторые из которых применяются, а другие не применяются.
  • Связанные классы дескрипторов объектов модели в django.db.models.fields.related (частный API) перемещены из модуля related в related_descriptors и переименованы следующим образом:
    • ReverseSingleRelatedObjectDescriptor это ForwardManyToOneDescriptor
    • SingleRelatedObjectDescriptor это ReverseOneToOneDescriptor
    • ForeignRelatedObjectsDescriptor это ReverseManyToOneDescriptor
    • ManyRelatedObjectsDescriptor это ManyToManyDescriptor
  • Если вы реализуете пользовательское представление handler404, оно должно возвращать ответ с кодом состояния HTTP 404. Используйте HttpResponseNotFound или передайте status=404 в HttpResponse. В противном случае APPEND_SLASH не будет корректно работать с DEBUG=False.

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

assignment_tag()

Django 1.4 добавил помощник assignment_tag для облегчения создания тегов шаблонов, которые хранят результаты в переменной шаблона. Помощник simple_tag() получил такую же возможность, сделав assignment_tag устаревшим. Теги, использующие assignment_tag, должны быть обновлены для использования simple_tag.

Синтаксис {% cycle %} с аргументами, разделенными запятыми

Тег cycle поддерживает устаревший синтаксис из предыдущих версий Django:

{% cycle row1,row2,row3 %}

Его разбор вызвал ошибки в текущем синтаксисе, поэтому поддержка старого синтаксиса будет удалена в Django 1.10 после ускоренной депривации.

ForeignKey и OneToOneField on_delete аргумент

Чтобы повысить осведомленность о каскадном удалении моделей, в Django 2.0 будет требоваться аргумент on_delete от ForeignKey и OneToOneField.

Обновите модели и существующие миграции для явного задания аргумента. Поскольку по умолчанию используется models.CASCADE, добавьте on_delete=models.CASCADE ко всем ForeignKey и OneToOneField, которые не используют другую опцию. Вы также можете передать его в качестве второго позиционного аргумента, если вас не волнует совместимость со старыми версиями Django.

Field.rel изменения

Field.rel и его методы и атрибуты изменены, чтобы соответствовать API связанных полей. Атрибут Field.rel переименован в remote_field, а многие его методы и атрибуты либо изменены, либо переименованы.

Цель этих изменений - предоставить документированный API для полей отношений.

GeoManager и GeoQuerySet пользовательские методы

Все пользовательские методы GeoQuerySet (area(), distance(), gml(), …) были заменены эквивалентными географическими выражениями в аннотациях (см. в новых возможностях). Следовательно, необходимость задавать пользовательский GeoManager для моделей с поддержкой ГИС теперь отпала. Как только ваш код не будет вызывать ни один из устаревших методов, вы можете просто удалить строки objects = GeoManager() из ваших моделей.

API загрузчика шаблонов изменились

Загрузчики шаблонов Django были обновлены, чтобы позволить рекурсивное расширение шаблонов. Это изменение привело к необходимости создания нового API загрузчика шаблонов. Старые методы load_template() и load_template_sources() теперь устарели. Подробности о новом API можно найти в разделе in the template loader documentation.

Передача 3-кортежа или app_name в include()

Часть передачи кортежа в качестве аргумента в include() была заменена передачей аргумента namespace в include(). Например:

polls_patterns = [
     url(...),
]

urlpatterns = [
    url(r'^polls/', include((polls_patterns, 'polls', 'author-polls'))),
]

становится:

polls_patterns = ([
     url(...),
], 'polls')  # 'polls' is the app_name

urlpatterns = [
    url(r'^polls/', include(polls_patterns, namespace='author-polls')),
]

Аргумент app_name для include() был заменен передачей кортежа 2 (как указано выше), или передачей объекта или модуля с атрибутом app_name (как указано ниже). Если app_name задан этим новым способом, аргумент namespace больше не требуется. По умолчанию он будет иметь значение app_name. Например, шаблоны URL в учебнике изменены с:

mysite/urls.py
urlpatterns = [
    url(r'^polls/', include('polls.urls', namespace="polls")),
    ...
]

к:

mysite/urls.py
urlpatterns = [
    url(r'^polls/', include('polls.urls')),  # 'namespace="polls"' removed
    ...
]
polls/urls.py
app_name = 'polls'  # added
urlpatterns = [...]

Это изменение также означает, что старый способ включения экземпляра AdminSite устарел. Вместо этого передавайте admin.site.urls непосредственно в django.conf.urls.url():

urls.py
from django.conf.urls import url
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),
]

Пространство имен приложения URL, необходимое при установке пространства имен экземпляра

В прошлом пространство имен экземпляра без пространства имен приложения служило той же цели, что и пространство имен приложения, но при наличии пространства имен приложения с тем же именем невозможно было отменить шаблоны. Включения, указывающие пространство имен экземпляра, требуют, чтобы включенный URLconf устанавливал пространство имен приложения.

current_app параметр к contrib.auth представлениям

Все представления в django.contrib.auth.views имеют следующую структуру:

def view(request, ..., current_app=None, ...):

    ...

    if current_app is not None:
        request.current_app = current_app

    return TemplateResponse(request, template_name, context)

Начиная с версии Django 1.8, current_app устанавливается на объект request. Для согласованности, эти представления будут требовать от вызывающего установить current_app на request вместо того, чтобы передавать его в отдельном аргументе.

django.contrib.gis.geoip

Модуль django.contrib.gis.geoip2 заменяет модуль django.contrib.gis.geoip. Новый модуль предоставляет аналогичный API, за исключением того, что он не предоставляет методы совместимости со старым GeoIP-Python API.

Разное

  • Аргумент weak для django.dispatch.signals.Signal.disconnect() был устаревшим, так как он не имеет никакого эффекта.
  • Метод check_aggregate_support() django.db.backends.base.BaseDatabaseOperations был устаревшим и будет удален в Django 2.0. Вместо него следует использовать более общий check_expression_support().
  • django.forms.extras является устаревшим. Вместо этого вы можете найти SelectDateWidget в django.forms.widgets (или просто django.forms).
  • Приватный API django.db.models.fields.add_lazy_relation() устарел.
  • Декоратор django.contrib.auth.tests.utils.skipIfCustomUser() устарел. С изменениями в обнаружении тестов в Django 1.6, тесты для приложений django.contrib больше не запускаются как часть проекта пользователя. Поэтому декоратор @skipIfCustomUser больше не нужен для декорирования тестов в django.contrib.auth.
  • Если вы настроили некоторые error handlers, сигнатуры представлений только с одним параметром запроса устарели. Теперь представления должны также принимать второй позиционный параметр exception.
  • Атрибуты django.utils.feedgenerator.Atom1Feed.mime_type и django.utils.feedgenerator.RssFeed.mime_type устарели в пользу content_type.
  • Signer теперь выдает предупреждение, если используется недопустимый разделитель. Это станет исключением в Django 1.10.
  • django.db.models.Field._get_val_from_obj() устарело в пользу Field.value_from_object().
  • django.template.loaders.eggs.Loader устарел, так как распространение приложений в виде яиц не рекомендуется.
  • Аргумент callable_obj в виде ключевого слова SimpleTestCase.assertRaisesMessage() является устаревшим. Вместо этого передавайте вызываемый объект в качестве позиционного аргумента.
  • Атрибут allow_tags для методов ModelAdmin был устаревшим. Вместо этого используйте format_html(), format_html_join() или mark_safe() при построении возвращаемого значения метода.
  • Аргумент enclosure с ключевым словом SyndicationFeed.add_item() устарел. Используйте новый аргумент enclosures, который принимает список объектов Enclosure вместо одного.
  • Псевдонимы django.template.loader.LoaderOrigin и django.template.base.StringOrigin для django.template.base.Origin устарели.

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

Эти функции достигли конца своего цикла устаревания и будут удалены в Django 1.9. Смотрите Функции, устаревшие в версии 1.7 для подробностей, включая то, как удалить использование этих функций.

  • django.utils.dictconfig удаляется.
  • django.utils.importlib удаляется.
  • django.utils.tzinfo удаляется.
  • django.utils.unittest удаляется.
  • Команда syncdb удалена.
  • django.db.models.signals.pre_syncdb и django.db.models.signals.post_syncdb удаляется.
  • Устранена поддержка allow_syncdb на маршрутизаторах баз данных.
  • Убрана автоматическая синхронизация приложений без миграций. Миграции обязательны для всех приложений, если только вы не передадите параметр migrate --run-syncdb.
  • Команды управления SQL для приложений без миграций, sql, sqlall, sqlclear, sqldropindexes и sqlindexes, удалены.
  • Устранена поддержка автоматической загрузки приспособлений initial_data и начальных данных SQL.
  • Все модели должны быть определены внутри установленного приложения или объявлены явным образом app_label. Более того, невозможно импортировать их до загрузки приложения. В частности, невозможно импортировать модели внутри корневого пакета приложения.
  • Модель и форма IPAddressField удалены. Поле-заглушка остается для совместимости с историческими миграциями.
  • AppCommand.handle_app() больше не поддерживается.
  • RequestSite и get_current_site() больше не импортируются из django.contrib.sites.models.
  • Удалена поддержка FastCGI через команду управления runfcgi.
  • django.utils.datastructures.SortedDict удаляется.
  • ModelAdmin.declared_fieldsets удаляется.
  • Модули util, которые обеспечивали обратную совместимость, удалены:
    • django.contrib.admin.util
    • django.contrib.gis.db.backends.util
    • django.db.backends.util
    • django.forms.util
  • ModelAdmin.get_formsets удаляется.
  • Устранена обратная совместимость, введенная для переименования метода BaseMemcachedCache._get_memcache_timeout() в get_backend_timeout().
  • Опции --natural и -n для dumpdata удалены.
  • Аргумент use_natural_keys для serializers.serialize() удален.
  • Частный API django.forms.forms.get_declared_fields() удален.
  • Возможность использовать SplitDateTimeWidget с DateTimeField удалена.
  • Свойство WSGIRequest.REQUEST удалено.
  • Класс django.utils.datastructures.MergeDict удаляется.
  • Языковые коды zh-cn и zh-tw удалены.
  • Внутренний django.utils.functional.memoize() удаляется.
  • django.core.cache.get_cache удаляется.
  • django.db.models.loading удаляется.
  • Передача вызываемых аргументов в querysets больше невозможна.
  • BaseCommand.requires_model_validation удалено в пользу requires_system_checks. Валидаторы администратора заменены на проверки администратора.
  • Атрибуты ModelAdmin.validator_class и default_validator_class удаляются.
  • ModelAdmin.validate() удаляется.
  • django.db.backends.DatabaseValidation.validate_field удаляется в пользу метода check_field.
  • Команда управления validate удалена.
  • django.utils.module_loading.import_by_path удаляется в пользу django.utils.module_loading.import_string.
  • Теги шаблонов ssi и url удалены из библиотеки тегов шаблонов future.
  • django.utils.text.javascript_quote() удаляется.
  • Настройки тестирования базы данных в виде независимых записей в настройках базы данных с префиксом TEST_ больше не поддерживаются.
  • Опция cache_choices для ModelChoiceField и ModelMultipleChoiceField удалена.
  • Значение по умолчанию атрибута RedirectView.permanent изменилось с True на False.
  • django.contrib.sitemaps.FlatPageSitemap удаляется в пользу django.contrib.flatpages.sitemaps.FlatPageSitemap.
  • Частный API django.test.utils.TestTemplateLoader удален.
  • Модуль django.contrib.contenttypes.generic удаляется.
Вернуться на верх