Вышел плановый релиз Django 1.11 с долговременной поддержкой

Выждав благоразумно трое суток после дня дурака, команда мантейнеров Django обрадовала нас ожидаемым новым LTS-релизом. С поддержкой аж до апреля 2020.

План выпуска релизов Django
План выпуска релизов с djangoproject.com

Данный релиз станет прощальным для 2-го Python`а.  Во всех последующих выпусках требования в версии Python - не ниже 3.5.  Заветы папы Гвидо восприняты всерьез. Вместе с тем, версия 1.11 стала первой, где поддерживается Python 3.6.

Мажорные новшества Django 1.11 LTS

Как-то скромненько...

Класс для создания индексов БД

Во фреймворке появился новый модуль django.db.models.indexes, предоставляющий класс Index. Использовать новый функционал нужно будет в подклассе Meta основного класса модели.

# models.py
from django.db import models
class Man(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=60)
  
    class Meta:
        indexes = [
            models.Index(fields=['last_name', 'first_name']),
            models.Index(fields=['first_name'], name='first_name_idx'),
        ]

Subquery и Exists для создания явных подзапросов

Теперь можно штатными средствами использовать в подзапросах ссылки на другие, "внешние",  queryset`ы, используя класс OuterRef.

from django.db.models import OuterRef, Subquery
  
newest = Comment.objects.filter(post=OuterRef('pk')).order_by('-created_at')
Post.objects.annotate(newest_commenter_email=Subquery(newest.values('email')[:1]))

Рендеринг посредством шаблонного движка (template-based)

До этого виджеты рендерились непосредственно самим Python. Теперь эту работа возложена на шаблонизатор. Виджеты теперь изменять под свои нужды будет гораздо проще и удобней, нежели раньше.  Жаждущим подробностей порекомендую посетить страницу документации с описанием нового Rndering API.

"Мелкие" нововведения

Этих тут целая портянка. Все рассматривать не стану, пробегусь лишь по тем, что "зацепили" глаз.

Авторизация

  • Упразднены функции-вьюхи login() и logout(). В угоду класс-ориентированным LoginView и LogoutView. Так же поступили и с функциями для манипуляции с паролями: все они заменены и свой class-based аналоги.
  • Улучшен и расширен функционал "сброса" и изменения пользовательских паролей.
  • В сигнал user_login_failed теперь будет передаваться объект запроса.  Наконец  то. Зачем он без запроса вообще был нужен.
  • Функция get_user_model отныне может вызываться при импорте. Даже в тех модулях, где описывается модель.

Postgres

  • Поле типа JSONField станет отныне принимать новый параметр encoder для указания настраиваемого класса для кодирования типов данных, не поддерживаемых стандартным кодером.
  • В полях HStoreField и HStoreField теперь можно хранить значение NULL.
  • Добавлен аргумент skip_locked для QuerySet.select_for_update() (PostgreSQL 9.5+ и Oracle) для выполнения запросов с FOR UPDATE SKIP LOCKED.
  • В настройки бэкенда БД добавлен параметр TEST['TEMPLATE']. Теперь при использовании PostgreSQL можно задавать шаблон для создания тестовой базы данных.
  • QuerySet.iterator() теперь использует server-side курсоры. Что может увеличить размер оперативной памяти, используемой БД.

Формы

  • У CharField появился новый аттрибут empty_value, куда можно задавать значение, когда поле не заполнено.
  • С новым методом Form.get_initial_for_field() мы можем теперь получить "начальные" данные для поля формы.

Модели

  • Добавлена поддержка "вызываемых" значений в аргументе по умолчанию для QuerySet.update_or_create() и get_or_create().
  • У  ImageField появился дефолтный  валидатор validate_image_file_extension.
  • Появилась поддержка выражений в методах QuerySet.values() и values_list().
  • В FileField теперь разрешено использовать "unique=True".
  • У объекта QuerySet появились методы union(), intersection(), и difference().

Requests и Responses

  • CommonMiddleware теперь выставляет Content-Length в заголовок  для non-streaming ответов.
  • ConditionalGetMiddleware будет теперь добавлять в ответы заголовок ETag.

Шаблоны

  • Функцию mark_safe() отныне можно использовать в качестве декоратора.
  • При использовании Jinja2 теперь возможно использовать контекстные процессоры, прописав их в 'context_processors' в OPTIONS.
  • Добавлен тег resetcycle для сброса внутри цикла в шаблоне.

Критические изменения, не поддерживаемые в предыдущих версиях Django

GIS

  • В целях упрощения кода библиотека GDAL теперь в зависимостях для GeoDjango. Ранее ее установка требовалась лишь для SQLite.
  • Удалены Contrib.gis.maps, поскольку данный функционал рассчитан на работу с устаревшей версией Google Maps API.
  • OpenLayers-based виджеты форм теперь используют OpenLayers 3. Так же обновлены шаблоны gis/openlayers.html и gis/openlayers-osm.html.
  • SpatiaLite версии ниже 4.0 и GDAL версий 1.7 и 1.8 более не поддерживаются.

Статика

  • Команда collectstatic может работать с ошибками при использовании хешированного хранилища, во время обработки в цикле (например, «foo.css» ссылается на «bar.css», который сам ссылается на «foo.css»). Или если иерархия цепочки файлов, чересчур глубока, чтобы пройтись по ним за несколько проходов. В этом случае советуют увеличить количество проходов, используя ManifestStaticFilesStorage.max_post_process_passes.
  • При использовании ManifestStaticFilesStorage статические файлы, не найденные в файле манифеста, теперь вызывают ValueError. Можно вернуться к старому поведению, установив для ManifestStaticFilesStorage.manifest_strict в значение False.

API баз данных

  • Для возможности использования усеченных данных в полях типа TimeField добавлен метод DatabaseOperations.time_trunc_sql(). Он принимает аргументы типа lookup_type и field_name,  возвращая соответствующий SQL-код для преобразования данных усеченного поля со временем/датой в специальный объект. Аргументом lookup_type может быть 'hour', 'minute' или 'second'.
  • Метод DatabaseOperations.datetime_cast_time_sql() добавлен для поддержки поиска по времени. Он принимает аргументы field_name и tzname и возвращает SQL, необходимый для преобразования значения даты/времени в значение времени.
  • Новый атрибут DatabaseFeatures.supports_index_column_ordering указывает, может ли БД сама определять порядок сортировки столбцов в индексах. Значение по умолчанию - True. Метод же DatabaseIntrospection.get_constraints() должен включать ключ порядка сортировки в каждом из возвращенных словарей со списком значений «ASC» и(или) «DESC», соответствующих порядку каждого столбца в индексе.
  • Inspectdb больше не вызывает устаревший метод DatabaseIntrospection.get_indexes().
  • Функция ignores_quoted_identifier_case переименована в ignores_table_name_case, что более точно отражает суть ее работы.
  • В метод DatabaseWrapper.create_cursor(self, name = None) добавлен аргумент name, чтобы была возможность использование курсоров на тех серверах, где они поддерживаются.

А так же...

  • PostgreSQL 9.2 и PostGIS 2.0 более не поддерживаются.
  • При работе приложения по протоколу HTTPS для LoginView, LogoutView и set_language() внедрена защита от переадресации на URL-адреса по незащищенному протоколу.
  • Функции get_or_create() и update_or_create() отныне проходят валидацию на предмет того, что их аргументы действительно являются полями модели.
  • Чтобы упростить работу с часовыми поясами,  библиотека pytz теперь является необходимой зависимостью, и будет автоматически устанавливается вместе с Django.
  • В связи с новой политикой рендеринга виджетов,  из django.forms.widgets удалены некоторые незадокументированные классы: SubWidget, RendererMixin, ChoiceFieldRenderer, RadioFieldRenderer, CheckboxFieldRenderer, ChoiceInput, RadioChoiceInput, CheckboxChoiceInput. Удален и метод Widget.format_output().
  • Django.Template.render() принимает теперь контекст только как словарь. Объект Context ему больше подсовывать нельзя.
  • Если ни один из элементов в RSS-фиде не имеет атрибута pubdate или updateddate, SyndicationFeed.latest_post_date() теперь вернет текущую дату/время в UTC-формате вместо даты и времени без информации о часовом поясе, как это было раньше.
  • ALLOWED_HOSTS при выполнении тестов теперь тоже будут проверяться.
  • В "модельных" формах CharField с null = True теперь сохраняет NULL для пустых значений вместо пустой строки.
  • Если вы реализуете подкласс AbstractUser с переопределением метода clean(), убедитесь, что в нем вызывается super(). BaseUserManager.normalize_email() вызывается в новом методе AbstractUser.clean(), чтобы нормализация применялась в  случаях типо проверки модели.
  • EmailField и URLField больше не принимают аргумент strip.
  • RelatedManager.add( ), remove(), clear() и set() теперь очищают кеш prefetch_related().
  • Чтобы предотвратить удаление сохраненных настроек, setup_test_environment() теперь вызывает исключение, если вызывается второй раз перед вызовом teardown_test_environment().
  • Подтверждение при удалении устаревшего типа контента больше не будет появляться после выполнения команды migrate. В таких ситуациях рекомендуеюся использовать новую команду remove_stale_contenttypes.
  • Виджет админки для IntegerField теперь использует type = "number" ( раньше было:  type = "text").
  • Get_model() и get_models() теперь вызывают исключение AppRegistryNotReady, если вызываются до загрузки моделей всех приложений. Раньше они требовали загрузки только моделей целевого приложения и, следовательно, могли возвращать модели без установления всех их отношений. Если хотите вернуть старое поведение для get_model(), установите для аргумента require_ready значение False.

Устаревший(deprecated) функционал

  • Декоратор models.permalink().  Взамен предлагается использовать django.urls.reverse().
  • Функция django.test.runner.setup_databases() перенесена в django.test.utils.setup_databases().
  • django.utils.translation.string_concat() объявлены устаревшими в пользу django.utils.text.format_lazy(). Функция string_concat(*strings) может быть заменена на format_lazy('{}' * len(strings), *strings).
  • Параметр host в django.utils.http.is_safe_url() устарел в пользу нового параметра allowed_hosts.
  • Model._meta.has_auto_field устарело в пользу проверки на то, что Model._meta.auto_field не None.
  • В метод Widget.render() добавлен аргумент renderer. Методы, которые не принимают этот аргумент будут так же работоспособны.
  • Параметр USE_ETAGS устарел в пользу ConditionalGetMiddleware, который теперь добавляет заголовок ETag к ответам независимо от настроек. CommonMiddleware и django.utils.cache.patch_response_headers() больше ETags устанавливать не будут.