Spec-Zone.ru › Django 2.1

Формы Аксессуары (класс Media)

Для отображения привлекательной и удобной формы веб-сайта требуется больше, чем просто HTML — также требуются таблицы стилей CSS, а если вы хотите использовать продвинутые виджеты «Веб 2.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 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 в объявлениях 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 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 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 настроено с помощью 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.

Изменено в Django 2.0:

В более старых версиях ресурсы объектов 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.1/topics/forms/media/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API