Активности форм (класс Media)
Для создания привлекательной и удобной веб-формы требуется не только HTML, но и CSS-стили, а для использования продвинутых виджетов «Web2.0» может потребоваться включить 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-файлов в объявлениях media: «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 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/3.2/topics/forms/media/