Ресурсы формы (класс Media)
Для создания привлекательной и удобной веб-формы недостаточно одного HTML — нужны также таблицы стилей CSS, а если вы хотите использовать сложные виджеты «Web 2.0», может потребоваться добавить JavaScript на каждую страницу. Точное сочетание CSS и JavaScript, необходимое для той или иной страницы, зависит от используемых на ней виджетов.
Здесь пригодятся определения ресурсов. Django позволяет связывать с формами и виджетами, которым нужны соответствующие ресурсы, различные файлы — например, таблицы стилей и скрипты. Например, если вы хотите использовать календарь для отображения DateFields, можно определить пользовательский виджет 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 используется в форме, Django укажет этой форме включить 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, необходимые для различных типов выходных данных.
Значения в словаре должны быть кортежем или списком имён файлов. Подробнее о том, как указывать пути к этим файлам, см. в разделе о путях.
Ключи словаря — это типы выходных носителей. Это те же типы, которые принимаются в объявлениях 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="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 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):
def _media(self):
return forms.Media(css={'all': ('pretty.css',)},
js=('animations.js', 'actions.js'))
media = property(_media)
Подробнее о создании возвращаемых значений для динамических свойств media см. в разделе Объекты Media.
Пути в определениях ресурсов
Пути, используемые для указания ресурсов, могут быть относительными или абсолютными. Если путь начинается с /, http:// или https://, он будет интерпретирован как абсолютный и оставлен без изменений. Ко всем остальным путям будет добавлено соответствующее значение префикса. Если установлено приложение django.contrib.staticfiles, оно будет использоваться для раздачи ресурсов.
Независимо от того, используете ли вы django.contrib.staticfiles, для отображения полной веб-страницы необходимы настройки STATIC_URL и STATIC_ROOT.
Чтобы определить подходящий префикс, Django проверит, что настройка STATIC_URL не равна None, и при необходимости автоматически переключится на использование 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 равно 'http://static.example.com/':
>>> 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 настроено с использованием ~django.contib.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>
В более ранних версиях ресурсы не раздавались с помощью django.contrib.staticfiles.
Объекты 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>
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/1.10/topics/forms/media/