Статические файлы форм (класс Media)¶
Rendering an attractive and easy-to-use Web form requires more than just HTML - it also requires CSS stylesheets, and if you want to use fancy «Web2.0» widgets, you may also need to include some JavaScript on each page. The exact combination of CSS and JavaScript that is required for any given page will depend upon the widgets that are in use on that page.
Здесь на помощь приходят определения ресурсов. Django позволяет связывать различные файлы, например таблицы стилей и скрипты, с формами и виджетами, которым требуются эти ресурсы. Например, если вы хотите использовать календарь для отображения DateFields, вы можете определить собственный виджет «Календарь». Затем этот виджет можно связать с CSS и JavaScript, необходимыми для отображения календаря. Когда виджет «Календарь» используется в форме, Django может идентифицировать необходимые файлы CSS и JavaScript и предоставить список имен файлов в форме, подходящей для включения на вашу веб-страницу.
Статические файлы интерфейса администратора Django
Поставляемое с Django приложение интерфейса администратора определяет ряд собственных виджетов для календарей, отфильтрованных выборок и так далее. Эти виджеты имеют свои требования к ресурсам. Шаблоны приложения подключают соответствующие файлы, которые необходимы для работы виджетов на странице.
Если вам понравились виджеты интерфейса администратора, то используйте их в своих приложениях. Они все расположены в 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.
This static definition is converted at runtime into a widget property
named media. The list of assets for a CalendarWidget instance
can be retrieved through this property:
>>> 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: „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',)
}
If this last CSS definition were to be rendered, it would become the following 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 базового класса.
By default, any object using a static Media definition will
inherit all the assets associated with the parent widget. This occurs
regardless of how the parent defines its own requirements. For
example, if we were to extend our basic Calendar widget from the
example above:
>>> 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>
The FancyCalendar widget inherits all the assets from its parent
widget. If you don’t want Media to be inherited in this way, add
an extend=False declaration to the Media declaration:
>>> 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, в том же формате, что используется при статическом определении ресурсов.
Например, вот как может быть представлено определение ресурсов для нашего виджета календаря в динамическом виде:
class CalendarWidget(forms.TextInput):
@property
def media(self):
return forms.Media(css={'all': ('pretty.css',)},
js=('animations.js', 'actions.js'))
Обратитесь к разделу Media objects для получения информации о том, как создать возвращаемые значения для динамических свойств ресурсов.
Определение путей до ресурсов¶
Paths used to specify assets can be either relative or absolute. If a
path starts with /, http:// or https://, it will be
interpreted as an absolute path, and left as-is. All other paths will
be prepended with the value of the appropriate prefix. If the
django.contrib.staticfiles app is installed, it will be used to serve
assets.
Независимо от того, используете ли вы django.contrib.staticfiles, для отображения страницы необходимо указать настройки STATIC_URL и STATIC_ROOT.
To find the appropriate prefix to use, Django will check if the
STATIC_URL setting is not None and automatically fall back
to using MEDIA_URL. For example, if the MEDIA_URL for
your site was 'http://uploads.example.com/' and STATIC_URL
was 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>
But if STATIC_URL is 'http://static.example.com/':
>>> 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>
Or if staticfiles is configured using the
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 виджета или формы в качестве значения будет возвращён экземпляр forms.Media. Как мы уже выяснили, строковым представлением такого объекта является HTML, который необходимо вставить в блок <head> вашей страницы.
Тем не менее, у объектов Media есть ещё интересные свойства.
Группы файлов¶
If you only want files of a particular type, you can use the subscript operator to filter out a medium of interest. For example:
>>> 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 objects can also be added together. When two Media objects are
added, the resulting Media object contains the union of the assets
specified by both:
>>> 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 Django пытается сохранить относительный порядок файлов, которые указаны в классе 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 для форм аналогичны правилам для виджетов: определения могут быть статическими или динамическими. Пути и правила наследования для этих определений тоже не отличаются.
Regardless of whether you define a media declaration, all Form
objects have a media property. The default value for this property
is the result of adding the media definitions for all widgets that
are part of the form:
>>> 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>
If you want to associate additional assets with a form – for example,
CSS for form layout – add a Media declaration to the form:
>>> 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>