Spec-Zone.ru › Django 2.2

Активности форм (класс 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 пустой и автоматически перейдёт к использованию 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, все объекты 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.2/topics/forms/media/

Spec-Zone.ru

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