Активности форм (класс 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" type="text/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-файлами в объявлениях медиа: ‘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 должно быть визуализировано, оно будет выглядеть следующим образом:
<link href="http://static.example.com/pretty.css" type="text/css" media="screen" rel="stylesheet"> <link href="http://static.example.com/lo_res.css" type="text/css" media="tv,projector" rel="stylesheet"> <link href="http://static.example.com/newspaper.css" type="text/css" media="print" rel="stylesheet">
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" type="text/css" media="all" rel="stylesheet">
<link href="http://static.example.com/fancy.css" type="text/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" type="text/css" media="all" rel="stylesheet">
<script src="http://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 для вашего сайта был '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" type="text/css" media="all" rel="stylesheet">
<script src="http://uploads.example.com/animations.js"></script>
<script src="http://othersite.com/actions.js"></script>
Но если STATIC_URL пустой:
>>> w = CalendarWidget() >>> print(w.media) <link href="/css/pretty.css" type="text/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" type="text/css" media="all" rel="stylesheet"> <script src="https://static.example.com/animations.27e20196a850.js"></script> <script src="http://othersite.com/actions.js"></script>
Media объекты
При обращении к атрибуту media виджета или формы возвращается объект forms.Media. Как мы уже видели, строковое представление объекта Media — это HTML, необходимый для включения соответствующих файлов в <head> блок вашей HTML-страницы.
Однако, объекты Media имеют и другие интересные свойства.
Подмножества активов
Если вам нужны только файлы определённого типа, вы можете использовать оператор среза для фильтрации интересующего медиа. Например:
>>> w = CalendarWidget() >>> print(w.media) <link href="http://static.example.com/pretty.css" type="text/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" type="text/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" type="text/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 объявление, все объекты 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="http://static.example.com/pretty.css" type="text/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" type="text/css" media="all" rel="stylesheet">
<link href="http://static.example.com/layout.css" type="text/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.0/topics/forms/media/