Активности форм (класс Media)
Для визуализации привлекательной и удобной веб-формы требуется не только HTML, но и CSS-таблицы стилей, а для использования удобных виджетов, возможно, потребуется также включить JavaScript на каждой странице. Точный набор CSS и JavaScript, необходимый для конкретной страницы, будет зависеть от используемых на ней виджетов.
Здесь на помощь приходят определения активов. Django позволяет связывать разные файлы — например, таблицы стилей и скрипты — с формами и виджетами, которым эти активы необходимы. Например, если вы хотите использовать календарь для визуализации полей типа DateField, можно определить пользовательский виджет Calendar. Этот виджет можно затем связать с CSS и JavaScript, необходимыми для визуализации календаря. Когда виджет Calendar используется в форме, Django может определить необходимые CSS и JavaScript-файлы и предоставить список имён файлов в формате, пригодном для включения на вашей веб-странице.
Активности и Django Admin
Приложение Django Admin определяет ряд настраиваемых виджетов для календарей, фильтрованных выборов и т. д. Эти виджеты определяют требования к активам, и Django Admin использует настраиваемые виджеты вместо стандартных Django. Шаблоны Admin будут включать только те файлы, которые необходимы для визуализации виджетов на данной странице.
Если вам нравятся виджеты, используемые приложением 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-файлы для различных типов выходных медиа.
Значения в словаре должны быть кортежами/списками имён файлов. Подробности о том, как указать пути к этим файлам, см. в разделе пути.
Ключами в словаре являются типы выходных медиа. Это те же типы, которые принимаются CSS-файлами в объявлениях медиа: ‘all’, ‘aural’, ‘braille’, ‘embossed’, ‘handheld’, ‘print’, ‘projection’, ‘screen’, ‘tty’ и ‘tv’. Если вам нужны разные таблицы стилей для разных типов медиа, укажите список CSS-файлов для каждого типа выходных медиа. Следующий пример предоставит два варианта CSS — один для экрана и один для печати:
class Media:
css = {
"screen": ["pretty.css"],
"print": ["newspaper.css"],
}
Если группа CSS-файлов подходит для нескольких типов выходных медиа, ключ словаря может быть списком типов выходных медиа, разделённых запятыми. В следующем примере TV и проекторы будут иметь одинаковые требования к медиа:
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 объекты
-
class Script(src, **attributes)[source] -
Представляет собой файл скрипта.
Первый параметр,
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 ключевые аргументы в том же формате, что и при статическом определении медиа.
Например, статическое определение для нашего виджета 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 пустой, и в противном случае автоматически использует 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 пуста:
>>> 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>
Добавлен класс объектов 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/5.2/topics/forms/media/