Активности форм (класс 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. Каждый раз, когда виджет Calendar используется в форме, эта форма будет направлена на включение файла CSS pretty.css, и файлов JavaScript animations.js и actions.js.
Это статическое определение преобразуется во время выполнения в свойство виджета с именем media. Список активов для экземпляра CalendarWidget можно получить через это свойство:
>>> w = CalendarWidget() >>> print(w.media) <link href="http://static.example.com/pretty.css" media="all" rel="stylesheet"> <script src="http://static.example.com/animations.js"></script> <script src="http://static.example.com/actions.js"></script>
Вот список всех возможных Media вариантов. Требуемых вариантов нет.
css
Словарь, описывающий необходимые файлы CSS для различных типов вывода.
Значения в словаре должны быть кортежем/списком имён файлов. См. раздел о путях для получения подробной информации о том, как указать пути к этим файлам.
Ключами в словаре являются типы вывода. Это те же типы, которые принимаются файлами CSS в объявлениях media: ‘all’, ‘aural’, ‘braille’, ‘embossed’, ‘handheld’, ‘print’, ‘projection’, ‘screen’, ‘tty’ и ‘tv’. Если вам нужно иметь разные таблицы стилей для разных типов вывода, укажите список файлов CSS для каждого типа вывода. Следующий пример предоставляет два варианта CSS — один для экрана и один для печати:
class Media:
css = {
"screen": ["pretty.css"],
"print": ["newspaper.css"],
}
Если группа файлов CSS подходит для нескольких типов вывода, ключ словаря может быть списком типов вывода, разделённых запятыми. В следующем примере ТВ и проекторы будут иметь одинаковые требования к media:
class Media:
css = {
"screen": ["pretty.css"],
"tv,projector": ["lo_res.css"],
"print": ["newspaper.css"],
}
Если это последнее определение CSS должно быть рендерировано, оно станет следующим HTML:
<link href="http://static.example.com/pretty.css" media="screen" rel="stylesheet"> <link href="http://static.example.com/lo_res.css" media="tv,projector" rel="stylesheet"> <link href="http://static.example.com/newspaper.css" media="print" rel="stylesheet">
В более старых версиях атрибут type="text/css" включался в ссылки на CSS.
js
Кортеж, описывающий необходимые файлы JavaScript. См. раздел о путях для получения подробной информации о том, как указать пути к этим файлам.
extend
Булево значение, определяющее поведение наследования для объявления Media.
По умолчанию любой объект, использующий статическое определение Media, унаследует все активы, связанные с родительским виджетом. Это происходит независимо от того, как родительский виджет определяет свои собственные требования. Например, если мы расширим наш базовый виджет Calendar из примера выше:
>>> class FancyCalendarWidget(CalendarWidget):
... class Media:
... css = {
... "all": ["fancy.css"],
... }
... js = ["whizbang.js"]
...
>>> w = FancyCalendarWidget()
>>> print(w.media)
<link href="http://static.example.com/pretty.css" media="all" rel="stylesheet">
<link href="http://static.example.com/fancy.css" media="all" rel="stylesheet">
<script src="http://static.example.com/animations.js"></script>
<script src="http://static.example.com/actions.js"></script>
<script src="http://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="http://static.example.com/fancy.css" media="all" rel="stylesheet">
<script src="http://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 пустой и автоматически вернётся к использованию MEDIA_URL. Например, если настройка MEDIA_URL для вашего сайта была 'http://uploads.example.com/', а STATIC_URL была None,:
>>> from django import forms
>>> class CalendarWidget(forms.TextInput):
... class Media:
... css = {
... "all": ["/css/pretty.css"],
... }
... js = ["animations.js", "http://othersite.com/actions.js"]
...
>>> w = CalendarWidget()
>>> print(w.media)
<link href="/css/pretty.css" media="all" rel="stylesheet">
<script src="http://uploads.example.com/animations.js"></script>
<script src="http://othersite.com/actions.js"></script>
Но если STATIC_URL равно 'http://static.example.com/',:
>>> w = CalendarWidget() >>> print(w.media) <link href="/css/pretty.css" media="all" rel="stylesheet"> <script src="http://static.example.com/animations.js"></script> <script src="http://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="http://othersite.com/actions.js"></script>
Пути как объекты
Пути к активам также могут быть заданы как хэшируемые объекты, реализующие метод __html__(). Метод __html__() обычно добавляется с помощью декоратора html_safe(). Объект отвечает за вывод полного содержимого HTML-тега <script> или <link>:
>>> from django import forms >>> from django.utils.html import html_safe >>> >>> @html_safe ... class JSPath: ... def __str__(self): ... return '<script src="https://example.org/asset.js" rel="stylesheet">' ... >>> class SomeWidget(forms.TextInput): ... class Media: ... js = [JSPath()] ...
Media объекты
При обращении к атрибуту media виджета или формы возвращается объект forms.Media. Как мы уже видели, строковое представление объекта Media — это HTML, необходимый для включения соответствующих файлов в блок <head> вашей HTML-страницы.
Однако, объекты Media имеют и другие интересные свойства.
Подмножества активов
Если вам нужны только файлы определённого типа, вы можете использовать оператор подстроки для фильтрации требуемого типа вывода. Например:
>>> w = CalendarWidget() >>> print(w.media) <link href="http://static.example.com/pretty.css" media="all" rel="stylesheet"> <script src="http://static.example.com/animations.js"></script> <script src="http://static.example.com/actions.js"></script> >>> print(w.media["css"]) <link href="http://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="http://static.example.com/pretty.css" media="all" rel="stylesheet">
<script src="http://static.example.com/animations.js"></script>
<script src="http://static.example.com/actions.js"></script>
<script src="http://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="http://static.example.com/jQuery.js"></script> <script src="http://static.example.com/calendar.js"></script> <script src="http://static.example.com/time.js"></script> <script src="http://static.example.com/noConflict.js"></script>
Объединение объектов Media с активами в конфликтующем порядке приводит к MediaOrderConflictWarning.
Media в формах
Виджеты не единственные объекты, которые могут иметь определения media — формы также могут иметь определения media. Правила для определений media в формах такие же, как для виджетов: объявления могут быть статическими или динамическими; правила путей и наследования для этих объявлений абсолютно такие же.
Независимо от того, определяете ли вы объявление media, все объекты формы имеют свойство 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="http://static.example.com/pretty.css" media="all" rel="stylesheet"> <script src="http://static.example.com/animations.js"></script> <script src="http://static.example.com/actions.js"></script> <script src="http://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="http://static.example.com/pretty.css" media="all" rel="stylesheet">
<link href="http://static.example.com/layout.css" media="all" rel="stylesheet">
<script src="http://static.example.com/animations.js"></script>
<script src="http://static.example.com/actions.js"></script>
<script src="http://static.example.com/whizbang.js"></script>
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/4.2/topics/forms/media/