Django 5. Рассмотрим новые возможности на примерах

09 Дек 2023 , 2507

4 декабря 2023 года состоялся релиз Django 5.0.

Django 5.0 поддерживает Python 3.10, 3.11, и 3.12. Поэтому настоятельно рекомендую обновляться на эти версии и идти ногу со временем. 

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

1. Значения по умолчанию, вычисленные базой данных (Database-Computed Default Values)

В Django 5 появилось новое свойство db_default для Field, которая позволяет БД указывать вычисляемые значения по умолчанию. Примечательно, что тикет с добавлением данной фичи был открыт еще 18 лет тому назад(некоторых читателей данной статьи даже в планах не было, а планы по данному тикету были) и наконец-то реализован в Django 5. Оперативность впечатляет!!!

Некоторые спросят, а зачем нам нужен db_default , ведь у нас есть свойство default , которая позволяет задавать значения по умолчанию? Чтобы ответить на этот вопрос придется на примерах все показать.

Давайте создадим две модели. Поля у моделей будут одинаковыми , но для первой модели для установки значений по умолчанию будем использовать default , а для второй модели db_default и значения по умолчанию будут вычисляться БД


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


# В этой модели значения по умолчанию
# будут устанавливаться через свойство default
class ShapeOne(models.Model):
    name = models.CharField(max_length=255)
    age = models.IntegerField(default=18)
    created = models.DateTimeField(default=Now())
    circumference = models.FloatField(default=2 * Pi())

    def __str__(self):
        return self.name


# В этой модели значения по умолчанию
# будут устанавливаться через свойство db_default
class ShapeTwo(models.Model):
    name = models.CharField(max_length=255)
    age = models.IntegerField(db_default=18)
    created = models.DateTimeField(db_default=Now())
    circumference = models.FloatField(db_default=2 * Pi())

    def __str__(self):
        return self.name


Создадим миграции и применим

Давайте посмотрим в БД

Django 5 Database-Computed Default Values

Как мы видим на рисунке , то теперь понятно какие значения будут проставлять по умолчанию в таблицу example_shapetwo и это становится прозрачным. Админастраторы баз данных или другие кто имеют доступ к БД понимают какие значения будут по умолчанию в таблице example_shapetwo , но не совсем понятно , какие значения будут по умолчанию в таблице example_shapeone

Рассмотрим DDL (язык описания данных) создания таблиц для обеих моделей в контексте Postgresql и увидим, что второй таблице данные по умолчанию будут вычисляются базой данных при вставке данных

Django 5

2.Генерируемые базой данных поля(Database Generated Model Field)

Еще одной крутой фичей , которую вы скорее всего будете использовать это GeneratedField. GeneratedField позволяет создать столбец в таблице , которая будет генерироваться базой данных на основе данных других столбцов этой таблицы.

Допустим у вас есть модель Square , где полями будут side и area.


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"), # Формула для вычисления площади, которая использует значения поля side
        output_field=models.BigIntegerField(),
        db_persist=True, #Если вы используете postgresql , тот тут всегда указывайте True
    )

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

Django 5

Django 5

В скриншоте мы видим , что вначале мы создаем запись , где сторона равна 2 и мы видим , что площадь правильно вычислился. Это делает сама база данных при вставке по формуле, которую мы указали в expression в GeneratedField.В Формуле мы просто перемножаем значение, которое хранится в поле side. В формулах мы можем использовать и другие поля данной модели, если это нужно. Далее мы в скриншоте проверяем , корректно ли обновляется значение area, если мы обновляем значение side.

Важно знать

1) В GeneratedField в expression мы можем использовать только поля данной модели(столбцы данной таблицы).

2) Сгенерированный столбец может быть виртуальным или сохраненным. Значения для виртуальных столбцов вычисляются сходу во время запроса, и они не занимают место для хранения. Значения для сохраненных столбцов предварительно вычисляются и сохраняются как часть данных таблицы. Так как postgresql не поддерживает виртуальных сгенерированнх столбцов , то вы не можете указать db_persist=False. Для postgresql всегда нужно указывать db_persist=True

3. Фасетные фильтры в админке (Facet filters in the admin)

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

Допустим у нас есть модель


class Shape(models.Model):
    name = models.CharField(max_length=255)
    age = models.IntegerField(default=18)
    created = models.DateTimeField(default=Now())
    circumference = models.FloatField(default=2 * Pi())

    def __str__(self):
        return self.name

И мы хотим в административной панели фильтровать по возрасту (age). Теперь в 5 версии Django добавили возможность показа счетчиков по фильтрам с помощью параметра show_facets , который может принимать два параметра:

admin.ShowFacets.ALLOW

admin.ShowFacets.ALWAYS

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


@admin.register(Shape)
class ShapeAdmin(admin.ModelAdmin):
    list_display = ('name', 'age', 'circumference')
    list_filter = ('age',)
    show_facets = admin.ShowFacets.ALLOW

Django 5 Facets show

Пояляется кнопка "Показать счетчики" и после нажатия мы видим сколько фигур есть по возрастам.Удобная и приятная вещь.

4. Упрощенные шаблоны для рендеринга полей формы

В Django 5.0 представлена концепция «группы полей» и шаблонов групп полей, упрощающая отрисовку полей формы Django. Такое упрощение значительно уменьшает объем кода HTML и шаблонов, необходимого для отображения элементов формы, делая ваши шаблоны более понятными и удобными в обслуживании.

C помощью as_field_group() мы можем лаконично записать для каждого поля формы код в представлении , как показано на скриншоте:

Django 5 Forms

as_field_group() отображает поля с помощью шаблона «django/forms/field.html» по умолчанию и может быть настроен для каждого проекта, для каждого поля или для каждого запроса.

5. Дополнительные возможности для Field choices

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


from django.db import models

#структура из двух кортежей
SPORT_CHOICES = [
    ("Martial Arts", [("judo", "Judo"), ("karate", "Karate")]),
    ("Racket", [("badminton", "Badminton"), ("tennis", "Tennis")]),
    ("unknown", "Unknown"),
]


class Winner(models.Model):
    name = models.CharField(max_length=255)
    sport = models.CharField(max_length=100, choices=SPORT_CHOICES)

В Django 5 мы можем использовать гораздо более краткое объявление, используя сопоставления словарей:


from django.db import models

# Использование словаря(отображения) вместо списка из двух кортежей
SPORT_CHOICES = {  
    "Martial Arts": {"judo": "Judo", "karate": "Karate"},
    "Racket": {"badminton": "Badminton", "tennis": "Tennis"},
    "unknown": "Unknown",
}


class Winner(models.Model):
    name = models.CharField(max_length=255)
    sport = models.CharField(max_length=100, choices=SPORT_CHOICES)

6. Обработка исключений при асинхронных отключениях

При асинхронных соединениях всегда существует риск того, что длительное соединение будет закрыто до того, как Django вернет ответ. Раньше не было встроенного механизма очистки при отмене асинхронного соединения.

Django 5 вызывает соответствующее исключение asyncio.CancelledError, которое вы можете перехватывать по мере необходимости.


async def my_view(request):
    try:
        # делаем полезную работу
        ...
    except asyncio.CancelledError:
        # Обрабатываем отключение
        raise

7. Поддержка декораторами асинхронных вьюх

Асинхронный ORM еще не подвезли , надеюсь появится в следующих релизах , но процесс движения в сторону асинхронности продолжается. И вот огромное количество декораторов поддерживают обертку асихронных вьюх:

  • cache_control()

  • never_cache()

  • no_append_slash()

  • csrf_exempt()

  • csrf_protect()

  • ensure_csrf_cookie()

  • requires_csrf_token()

  • sensitive_variables()

  • sensitive_post_parameters()

  • gzip_page()

  • condition()

  • conditional_page()

  • etag()

  • last_modified()

  • require_http_methods()

  • require_GET()

  • require_POST()

  • require_safe()

  • vary_on_cookie()

  • vary_on_headers()

  • xframe_options_deny()

  • xframe_options_sameorigin()

  • xframe_options_exempt()

Заключение

Вначале мне просто хотелось написать о релизе с кратким описание возможностей , которые были добавлены. Но потом решил подробно о них рассказать , чтобы было понятно мне и читателям и поэтму статья получилось такой объемной. Если у вас будут вопросы, то пишите в комментариях

comments powered by Disqus

Подписка

Подпишитесь на наш список рассылки, чтобы получать обновления из блога

Рубрики

Теги