Активности форм (класс Media)
Для отображения привлекательной и удобной веб-формы требуется не только HTML, но и таблицы стилей CSS, а если вы хотите использовать навороченные виджеты «Веб 2.0», вам также может потребоваться включить JavaScript на каждой странице. Точный набор CSS и JavaScript, необходимый для каждой страницы, будет зависеть от виджетов, используемых на этой странице.
Вот где вступают в игру определения активов. Django позволяет связывать различные файлы, такие как таблицы стилей и скрипты, с формами и виджетами, которым нужны эти активы. Например, если вы хотите использовать календарь для отображения полей DateField, вы можете определить пользовательский виджет календаря. Затем этот виджет можно связать с CSS и JavaScript, необходимыми для отображения календаря. Когда виджет календаря используется в форме, 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="http://static.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" /> <script type="text/javascript" src="http://static.example.com/animations.js"></script> <script type="text/javascript" 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 будет обработано, оно станет следующим HTML:
<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 определение, наследует все активы, связанные с родительским виджетом. Это происходит независимо от того, как родитель определяет свои собственные требования. Например, если мы расширим наш базовый виджет календаря из примера выше:
>>> 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 type="text/javascript" src="http://static.example.com/animations.js"></script>
<script type="text/javascript" src="http://static.example.com/actions.js"></script>
<script type="text/javascript" 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 type="text/javascript" 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 type="text/javascript" src="http://uploads.example.com/animations.js"></script>
<script type="text/javascript" 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 type="text/javascript" src="http://static.example.com/animations.js"></script> <script type="text/javascript" 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 type="text/javascript" src="https://static.example.com/animations.27e20196a850.js"></script> <script type="text/javascript" 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 type="text/javascript" src="http://static.example.com/animations.js"></script> <script type="text/javascript" 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 type="text/javascript" src="http://static.example.com/animations.js"></script>
<script type="text/javascript" src="http://static.example.com/actions.js"></script>
<script type="text/javascript" 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 type="text/javascript" src="http://static.example.com/jQuery.js"></script>
<script type="text/javascript" src="http://static.example.com/calendar.js"></script>
<script type="text/javascript" src="http://static.example.com/time.js"></script>
<script type="text/javascript" src="http://static.example.com/noConflict.js"></script>
Объединение объектов Media с активами в конфликтующем порядке приводит к ошибке MediaOrderConflictWarning.
В более ранних версиях активы объектов Media конкатенировались, а не объединялись таким образом, чтобы сохранить относительный порядок элементов в каждом списке.
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 type="text/javascript" src="http://static.example.com/animations.js"></script> <script type="text/javascript" src="http://static.example.com/actions.js"></script> <script type="text/javascript" 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 type="text/javascript" src="http://static.example.com/animations.js"></script>
<script type="text/javascript" src="http://static.example.com/actions.js"></script>
<script type="text/javascript" src="http://static.example.com/whizbang.js"></script>
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/2.0/topics/forms/media/