Активности форм (класс 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. Каждый раз, когда виджет CalendarWidget используется в форме, эта форма будет направлена на включение CSS-файла pretty.css, и JavaScript-файлов animations.js и actions.js.
Это статическое определение преобразуется во время выполнения в свойство виджета под названием media. Список активов для экземпляра CalendarWidget можно получить через это свойство:
>>> w = CalendarWidget() >>> print(w.media) <link href="https://static.example.com/pretty.css" media="all" rel="stylesheet"> <script src="https://static.example.com/animations.js"></script> <script src="https://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="https://static.example.com/pretty.css" media="screen" rel="stylesheet"> <link href="https://static.example.com/lo_res.css" media="tv,projector" rel="stylesheet"> <link href="https://static.example.com/newspaper.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="https://static.example.com/pretty.css" media="all" rel="stylesheet">
<link href="https://static.example.com/fancy.css" media="all" rel="stylesheet">
<script src="https://static.example.com/animations.js"></script>
<script src="https://static.example.com/actions.js"></script>
<script src="https://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="https://static.example.com/fancy.css" media="all" rel="stylesheet">
<script src="https://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 не None и автоматически вернётся к использованию MEDIA_URL. Например, если MEDIA_URL вашего сайта была 'https://uploads.example.com/' и STATIC_URL была None:
>>> from django import forms
>>> class CalendarWidget(forms.TextInput):
... class Media:
... css = {
... "all": ["/css/pretty.css"],
... }
... js = ["animations.js", "https://othersite.com/actions.js"]
...
>>> w = CalendarWidget()
>>> print(w.media)
<link href="/css/pretty.css" media="all" rel="stylesheet">
<script src="https://uploads.example.com/animations.js"></script>
<script src="https://othersite.com/actions.js"></script>
Но если STATIC_URL является 'https://static.example.com/':
>>> w = CalendarWidget() >>> print(w.media) <link href="/css/pretty.css" media="all" rel="stylesheet"> <script src="https://static.example.com/animations.js"></script> <script src="https://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="https://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" defer>' ... >>> class SomeWidget(forms.TextInput): ... class Media: ... js = [JSPath()] ...
Media объекты
При запросе атрибута media виджета или формы возвращается объект forms.Media. Как мы уже видели, строковое представление объекта Media — это HTML, необходимый для включения соответствующих файлов в <head> блок вашей HTML-страницы.
Однако, объекты Media обладают некоторыми другими интересными свойствами.
Подмножества активов
Если вам нужны только файлы определённого типа, вы можете использовать оператор подстроки для фильтрации среды. Например:
>>> w = CalendarWidget() >>> print(w.media) <link href="https://static.example.com/pretty.css" media="all" rel="stylesheet"> <script src="https://static.example.com/animations.js"></script> <script src="https://static.example.com/actions.js"></script> >>> print(w.media["css"]) <link href="https://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="https://static.example.com/pretty.css" media="all" rel="stylesheet">
<script src="https://static.example.com/animations.js"></script>
<script src="https://static.example.com/actions.js"></script>
<script src="https://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="https://static.example.com/jQuery.js"></script> <script src="https://static.example.com/calendar.js"></script> <script src="https://static.example.com/time.js"></script> <script src="https://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="https://static.example.com/pretty.css" media="all" rel="stylesheet"> <script src="https://static.example.com/animations.js"></script> <script src="https://static.example.com/actions.js"></script> <script src="https://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="https://static.example.com/pretty.css" media="all" rel="stylesheet">
<link href="https://static.example.com/layout.css" media="all" rel="stylesheet">
<script src="https://static.example.com/animations.js"></script>
<script src="https://static.example.com/actions.js"></script>
<script src="https://static.example.com/whizbang.js"></script>
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/5.1/topics/forms/media/