フォームアセット (Media クラス)Link to this heading
魅力的で使いやすいウェブのフォームを作るには、HTML だけでは不十分です - CSS スタイルシートも必要です。さらに豪華な "Web2.0" ウィジェットを使いたければ、JavaScript を各ページに配置する必要もあるでしょう。あるページに必要な CSS と JavaScript の組み合わせは、そのページに使用されるウィジェットによって異なります。
This is where asset definitions come in. Django allows you to associate different files -- like stylesheets and scripts -- with the forms and widgets that require those assets. For example, if you want to use a calendar to render DateFields, you can define a custom Calendar widget. This widget can then be associated with the CSS and JavaScript that is required to render the calendar. When the Calendar widget is used on a form, Django is able to identify the CSS and JavaScript files that are required, and provide the list of file names in a form suitable for inclusion on your Web page.
定数として定義されたアセットLink to this heading
アセットを定義する最も簡単な方法は、定数とすることです。この方法を利用するには、内部 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="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 オプションです。必須のものはありません。
cssLink to this heading
様々な形式の出力メディアに対して、必要な CSSファイルを定義するディクショナリです。
ディクショナリの値はファイル名のタプルかリストで指定します。ファイルのパスを指定する方法は パスの章 を参照してください。
ディクショナリのキーは、出力するメディアタイプを表します。これらは CSS ファイル認識可能なメディアタイプと同じです: 'all'、'aural'、'braille'、'embossed'、'handheld'、'print'、'projection'、'screen'、'tty'、'tv'。メディアタイプに応じたスタイルシートを提供する必要がある場合、各出力メディアに対して CSS ファイルを作成してください。以下の例は 2 つの CSS オプションを提供します -- 1 つはスクリーン用でもう一つは印刷用です:
class Media:
css = {
'screen': ('pretty.css',),
'print': ('newspaper.css',)
}
1 つのCSS ファイルのグループを複数の出力メディアタイプに適用するには、出力メディアタイプをカンマで区切ってディス書なりのキーに指定します。以下の例では、TV とプロジェクターは同じメディアを参照します:
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">
jsLink to this heading
必要な JavaScript ファイルを定義したタプルです。ファイルのパスを指定する方法は パスの章 を参照してください。
extendLink to this heading
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 を追加します:
>>> 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>
継承をより詳細にコントロールするにあh、動的なプロパティ を使ってアセットを定義してください。 動的なプロパティを使えば、どのファイルを継承し、また継承しないかを完全にコントロールできます。
動的プロパティとしての MediaLink to this heading
アセット要件のより詳細な操作が必要な場合、media プロパティを直接定義することができます。これは、forms.Media のインスタンスを返すウィジェットプロパティを定義することで実現できます。forms.Media に対するコンストラクタは、定数によるメディア定義と同様の形式で、css と js のキーワード引数を認識します。
例えば、上記例で扱ってきた Calendar Widget に対する定数の定義は、動的な方法では以下のように定義できます:
class CalendarWidget(forms.TextInput):
@property
def media(self):
return forms.Media(css={'all': ('pretty.css',)},
js=('animations.js', 'actions.js'))
動的な media プロパティに対する戻り値を構成する方法については、Media objects`_ を参照してください。
アセット定義内のパスLink to this heading
アセットを定義するためのファイルパスには、相対および絶対パスのどちらも使えます。パスが /、http://、https:// のいずれかで始まる場合は絶対パスとして認識され、そのまま使用されます。それ以外のパスには適当な接頭辞が追加されます。django.contrib.staticfiles アプリケーションがインストールされている場合、アセットを提供するために使用されます。
mod: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 objectsLink to this heading
ウィジェットやフォームの media 属性に応答指令信号を送ると、forms.Media オブジェクトが戻り値となります。すでに見たように、Media オブジェクトの文字列表現は HTMLで、HTML ページ内の <head> ブロックに関連ファイルを含める必要があります。
ただし、Media オブジェクトにはいくつかの面白いプロパティが存在します。
アセットのサブセットLink to this heading
特定のタイプのファイルのみ必要な場合、サブスクリプトオペレーターを使って使用するメディアをフィルタできます。例えば:
>>> 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 オブジェクトを結合するLink to this heading
Media オブジェクトを複数使用することもできます。2 つの 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>
アセットの順序Link to this heading
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 となります。
フォームの MediaLink to this heading
ウィジェットだけでなく、フォームにも 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>
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 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>