Spec-Zone.ru › Django 6.0

Ресурсы формы (класс Media)

Чтобы создать привлекательную и удобную веб-форму, недостаточно одного HTML — также нужны таблицы стилей CSS, а если вы хотите использовать сложные виджеты, возможно, потребуется подключить JavaScript на каждой странице. Точный набор CSS и JavaScript, необходимый для конкретной страницы, зависит от используемых на ней виджетов.

Здесь пригодятся определения ресурсов. Django позволяет связывать разные файлы — например, таблицы стилей и скрипты — с формами и виджетами, которым нужны эти ресурсы. Например, если вы хотите использовать календарь для отображения DateFields, можно определить собственный виджет Calendar. Затем этот виджет можно связать с CSS и JavaScript, необходимыми для отображения календаря. Когда виджет Calendar используется в форме, Django может определить необходимые файлы CSS и JavaScript и предоставить список их имён в формате, подходящем для включения в веб-страницу.

Ресурсы и панель администратора Django

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

Если вам нравятся виджеты, используемые приложением Django Admin, можете свободно использовать их в своём приложении! Все они хранятся в django.contrib.admin.widgets.

Какой инструментарий JavaScript выбрать?

Существует множество инструментариев JavaScript, многие из которых включают виджеты (например, виджеты календаря), которые можно использовать для расширения возможностей приложения. Django намеренно не отдаёт предпочтение какому-либо одному инструментарию JavaScript. У каждого инструментария есть свои сильные и слабые стороны — выбирайте тот, который подходит вашим требованиям. Django может интегрироваться с любым инструментарием JavaScript.

Ресурсы как статическое определение

Самый простой способ определить ресурсы — использовать статическое определение. При этом объявление задаётся во внутреннем классе Media. Свойства внутреннего класса определяют требования.

Вот пример:

from django import forms


class CalendarWidget(forms.TextInput):
    class Media:
        css = {
            "all": ["pretty.css"],
        }
        js = ["animations.js", "actions.js"]

Этот код определяет CalendarWidget на основе TextInput. Каждый раз, когда CalendarWidget используется в форме, форма будет включать CSS-файл pretty.css и файлы JavaScript animations.js и actions.js.

Это статическое определение во время выполнения преобразуется в свойство виджета с именем media. Список ресурсов экземпляра CalendarWidget можно получить через это свойство:

>>> w = CalendarWidget()
>>> print(w.media)
<link href="https://static.example.com/pretty.css" media="all" rel="stylesheet">
<script src="https://static.example.com/animations.js"></script>
<script src="https://static.example.com/actions.js"></script>

Ниже приведён список всех возможных параметров Media. Обязательных параметров нет.

css

Словарь, описывающий CSS-файлы, необходимые для различных типов выходных данных.

Значениями в словаре должны быть кортежи или списки имён файлов. Подробные сведения о том, как задавать пути к этим файлам, см. в разделе о путях.

Ключами словаря являются типы выходных носителей. Это те же типы, которые принимаются в объявлениях media CSS-файлов: ‘all’, ‘aural’, ‘braille’, ‘embossed’, ‘handheld’, ‘print’, ‘projection’, ‘screen’, ‘tty’ и ‘tv’. Если для разных типов носителей нужны разные таблицы стилей, укажите список CSS-файлов для каждого выходного носителя. Следующий пример задаёт два варианта CSS — один для экрана, другой для печати:

class Media:
    css = {
        "screen": ["pretty.css"],
        "print": ["newspaper.css"],
    }

Если группа CSS-файлов подходит для нескольких типов выходных носителей, ключом словаря может быть список типов выходных носителей, разделённых запятыми. В следующем примере для телевизоров и проекторов будут заданы одинаковые требования к носителям:

class Media:
    css = {
        "screen": ["pretty.css"],
        "tv,projector": ["lo_res.css"],
        "print": ["newspaper.css"],
    }

Если отобразить это последнее определение CSS, получится следующий HTML:

<link href="https://static.example.com/pretty.css" media="screen" rel="stylesheet">
<link href="https://static.example.com/lo_res.css" media="tv,projector" rel="stylesheet">
<link href="https://static.example.com/newspaper.css" media="print" rel="stylesheet">

js

Кортеж, описывающий необходимые файлы JavaScript. Подробные сведения о том, как задавать пути к этим файлам, см. в разделе о путях.

объекты Script

Добавлено в Django 5.2.
class Script(src, **attributes) [исходный код]

Представляет файл скрипта.

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

Необязательные именованные аргументы, **attributes, — это HTML-атрибуты, задаваемые для отображаемого тега <script>.

Примеры использования см. в разделе Пути в виде объектов.

extend

Логическое значение, задающее поведение наследования для объявлений Media.

По умолчанию любой объект, использующий статическое определение Media, наследует все ресурсы, связанные с родительским виджетом. Это происходит независимо от того, как родительский виджет определяет собственные требования. Например, если расширить базовый виджет Calendar из примера выше:

>>> class FancyCalendarWidget(CalendarWidget):
...     class Media:
...         css = {
...             "all": ["fancy.css"],
...         }
...         js = ["whizbang.js"]
...

>>> w = FancyCalendarWidget()
>>> print(w.media)
<link href="https://static.example.com/pretty.css" media="all" rel="stylesheet">
<link href="https://static.example.com/fancy.css" media="all" rel="stylesheet">
<script src="https://static.example.com/animations.js"></script>
<script src="https://static.example.com/actions.js"></script>
<script src="https://static.example.com/whizbang.js"></script>

Виджет FancyCalendar наследует все ресурсы родительского виджета. Если вы не хотите, чтобы Media наследовались таким образом, добавьте объявление extend=False в объявление Media:

>>> class FancyCalendarWidget(CalendarWidget):
...     class Media:
...         extend = False
...         css = {
...             "all": ["fancy.css"],
...         }
...         js = ["whizbang.js"]
...

>>> w = FancyCalendarWidget()
>>> print(w.media)
<link href="https://static.example.com/fancy.css" media="all" rel="stylesheet">
<script src="https://static.example.com/whizbang.js"></script>

Если вам нужен ещё больший контроль над наследованием, задайте ресурсы с помощью динамического свойства. Динамические свойства дают полный контроль над тем, какие файлы наследуются, а какие — нет.

Media как динамическое свойство

Если требуется более сложная обработка требований к ресурсам, можно определить свойство media напрямую. Для этого нужно определить свойство виджета, возвращающее экземпляр forms.Media. Конструктор forms.Media принимает именованные аргументы css и js в том же формате, что и статическое определение media.

Например, статическое определение виджета Calendar можно также задать динамически:

class CalendarWidget(forms.TextInput):
    @property
    def media(self):
        return forms.Media(
            css={"all": ["pretty.css"]}, js=["animations.js", "actions.js"]
        )

Дополнительные сведения о создании возвращаемых значений для динамических свойств media см. в разделе Объекты Media.

Пути в определениях ресурсов

Пути в виде строк

Строковые пути, используемые для указания ресурсов, могут быть относительными или абсолютными. Если путь начинается с /, http:// или https://, он интерпретируется как абсолютный и остаётся без изменений. Перед всеми остальными путями добавляется значение соответствующего префикса. Если установлено приложение django.contrib.staticfiles, оно будет использоваться для обслуживания ресурсов.

Независимо от того, используете ли вы django.contrib.staticfiles, для отображения полной веб-страницы необходимы настройки STATIC_URL и STATIC_ROOT.

Чтобы определить подходящий префикс, Django проверит, что настройка STATIC_URL не равна None, и автоматически перейдёт к использованию MEDIA_URL. Например, если для вашего сайта MEDIA_URL равен 'https://uploads.example.com/', а STATIC_URL равен None:

>>> from django import forms
>>> class CalendarWidget(forms.TextInput):
...     class Media:
...         css = {
...             "all": ["/css/pretty.css"],
...         }
...         js = ["animations.js", "https://othersite.com/actions.js"]
...

>>> w = CalendarWidget()
>>> print(w.media)
<link href="/css/pretty.css" media="all" rel="stylesheet">
<script src="https://uploads.example.com/animations.js"></script>
<script src="https://othersite.com/actions.js"></script>

Но если STATIC_URL равен 'https://static.example.com/':

>>> w = CalendarWidget()
>>> print(w.media)
<link href="/css/pretty.css" media="all" rel="stylesheet">
<script src="https://static.example.com/animations.js"></script>
<script src="https://othersite.com/actions.js"></script>

Или если staticfiles настроено с использованием ManifestStaticFilesStorage:

>>> w = CalendarWidget()
>>> print(w.media)
<link href="/css/pretty.css" media="all" rel="stylesheet">
<script src="https://static.example.com/animations.27e20196a850.js"></script>
<script src="https://othersite.com/actions.js"></script>

Пути в виде объектов

Ресурсы также могут задаваться в виде объектов с использованием Script. Кроме того, такой способ позволяет передавать пользовательские HTML-атрибуты:

class Media:
    js = [
        Script(
            "https://cdn.example.com/something.min.js",
            **{
                "crossorigin": "anonymous",
                "async": True,
            },
        ),
    ]

Если отобразить это определение Media, получится следующий HTML:

<script src="https://cdn.example.com/something.min.js"
        crossorigin="anonymous"
        async>
</script>
Изменено в Django 5.2:

Добавлен класс объектов Script.

Объекты Media

При обращении к атрибуту media виджета или формы возвращается объект forms.Media. Как мы уже видели, строковое представление объекта Media — это HTML, необходимый для включения соответствующих файлов в блок <head> вашей HTML-страницы.

Однако объекты Media обладают и другими интересными свойствами.

Подмножества ресурсов

Если нужны файлы только определённого типа, можно использовать оператор индексации, чтобы отфильтровать нужный тип носителя. Например:

>>> w = CalendarWidget()
>>> print(w.media)
<link href="https://static.example.com/pretty.css" media="all" rel="stylesheet">
<script src="https://static.example.com/animations.js"></script>
<script src="https://static.example.com/actions.js"></script>

>>> print(w.media["css"])
<link href="https://static.example.com/pretty.css" media="all" rel="stylesheet">

При использовании оператора индексации возвращается новый объект Media, содержащий только нужные типы носителей.

Объединение объектов Media

Объекты Media также можно складывать. При сложении двух объектов Media результирующий объект Media содержит объединение ресурсов, указанных в обоих объектах:

>>> from django import forms
>>> class CalendarWidget(forms.TextInput):
...     class Media:
...         css = {
...             "all": ["pretty.css"],
...         }
...         js = ["animations.js", "actions.js"]
...

>>> class OtherWidget(forms.TextInput):
...     class Media:
...         js = ["whizbang.js"]
...

>>> w1 = CalendarWidget()
>>> w2 = OtherWidget()
>>> print(w1.media + w2.media)
<link href="https://static.example.com/pretty.css" media="all" rel="stylesheet">
<script src="https://static.example.com/animations.js"></script>
<script src="https://static.example.com/actions.js"></script>
<script src="https://static.example.com/whizbang.js"></script>

Порядок ресурсов

Порядок добавления ресурсов в DOM часто имеет значение. Например, у вас может быть скрипт, зависящий от jQuery. Поэтому при объединении объектов Media сохраняется относительный порядок, в котором ресурсы определены в каждом классе Media.

Например:

>>> from django import forms
>>> class CalendarWidget(forms.TextInput):
...     class Media:
...         js = ["jQuery.js", "calendar.js", "noConflict.js"]
...
>>> class TimeWidget(forms.TextInput):
...     class Media:
...         js = ["jQuery.js", "time.js", "noConflict.js"]
...
>>> w1 = CalendarWidget()
>>> w2 = TimeWidget()
>>> print(w1.media + w2.media)
<script src="https://static.example.com/jQuery.js"></script>
<script src="https://static.example.com/calendar.js"></script>
<script src="https://static.example.com/time.js"></script>
<script src="https://static.example.com/noConflict.js"></script>

Объединение объектов Media с ресурсами в конфликтующем порядке приводит к MediaOrderConflictWarning.

Media у форм

Не только у виджетов могут быть определения media — формы тоже могут задавать media. Правила для определений media у форм такие же, как и для виджетов: объявления могут быть статическими или динамическими; правила задания путей и наследования для этих объявлений полностью совпадают.

Независимо от того, определяете ли вы объявление media, у всех объектов Form есть свойство media. Значение этого свойства по умолчанию получается в результате сложения определений media для всех виджетов, входящих в форму:

>>> from django import forms
>>> class ContactForm(forms.Form):
...     date = DateField(widget=CalendarWidget)
...     name = CharField(max_length=40, widget=OtherWidget)
...

>>> f = ContactForm()
>>> f.media
<link href="https://static.example.com/pretty.css" media="all" rel="stylesheet">
<script src="https://static.example.com/animations.js"></script>
<script src="https://static.example.com/actions.js"></script>
<script src="https://static.example.com/whizbang.js"></script>

Если вы хотите связать с формой дополнительные ресурсы — например, CSS для оформления формы, — добавьте объявление Media в форму:

>>> class ContactForm(forms.Form):
...     date = DateField(widget=CalendarWidget)
...     name = CharField(max_length=40, widget=OtherWidget)
...     class Media:
...         css = {
...             "all": ["layout.css"],
...         }
...

>>> f = ContactForm()
>>> f.media
<link href="https://static.example.com/pretty.css" media="all" rel="stylesheet">
<link href="https://static.example.com/layout.css" media="all" rel="stylesheet">
<script src="https://static.example.com/animations.js"></script>
<script src="https://static.example.com/actions.js"></script>
<script src="https://static.example.com/whizbang.js"></script>

© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/6.0/topics/forms/media/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API