.. _topics-forms-media:
フォームメディア( Form Media )
==============================
:revision-up-to: 17812 (1.4)
魅力的で使いやすい Web フォームをレンダしたいなら、 HTML だけでは能力不足で
しょう。CSS スタイルシートが必要でしょうし、ファンシーな "Web2.0" 的ウィジェッ
トを使いたければ、各ページに JavaScript を仕込む必要もあるでしょう。あるペー
ジでどのCSS と JavaScript の組み合わせが必要かは、そのページで使われている
ウィジェットによって異なります。
そこで、 Django のメディア定義が登場します。 Django は様々なメディアファイ
ルをそのメディアファイルを必要とするフォームやウィジェットに関連付けられる
ようにしています。例えば、カレンダーを使って DateField をレンダしたいので、
カスタムの Calendar ウィジェットを定義したとします。このウィジェットには、
カレンダーをレンダするときに必要な CSS や JavaScript を関連付けておけます。
Calendar ウィジェットをフォーム上で使うときに、 Django は必要な CSS や
JavaScript ファイルを特定して、それらのファイル名のリストを Web ページに簡
単に組み込める形式で提供します。
.. admonition:: Django admin とメディアファイル
Django の admin アプリケーションは、カレンダーやフィルタ機能つきのセレ
クタといった、様々なカスタムウィジェットを定義しています。これらのウィ
ジェットは、それぞれが必要なメディアを定義していて、 Django は admin サ
イトを表示するときに、デフォルトのウィジェットの代りにカスタムウィジェッ
トを表示しています。 admin のテンプレートは、ページごとに必要なメディア
ファイルだけを取り込み、ウィジェットをレンダするようになっています。
Django の admin アプリケーションのウィジェットを使いたいのなら、どうぞ
ご自由に!ウィジェットは、全て ``django.contrib.admin.widgets`` で定義
されています。
.. admonition:: どの JavaScript ツールキットを使えばいいの ?
JavaScript ツールキットは世の中に沢山あり、そのほとんどが(カレンダーの
ような)アプリケーションの操作性を向上させられるウィジェットを定義して
います。 Django は特定の JavaScript ツールキットを贔屓にしないよう慎重
に設計されています。どのツールキットにも、お互いに長所や短所があります。
自分にとって使いやすいツールキットを使ってください。 Django はどんな
JavaScript ツールキットでも組み込めます。
メディア定義を静的に行う
---------------------------
メディア定義の簡単な方法は、クラスで静的に行うというものです。この方法では、
メディア定義を内部クラスとして宣言します。必要なメディアファイルの情報は、
内部クラスのプロパティとして定義します。
簡単な例を示しましょう::
class CalendarWidget(forms.TextInput):
class Media:
css = {
'all': ('pretty.css',)
}
js = ('animations.js', 'actions.js')
このコードでは、 ``TextInput`` をベースにして ``CalendarWidget`` を定義して
います。 ``CalendarWidget`` をフォーム内で使うと、フォームは CSS ファイル
``pretty.css`` と JavaScript ファイル ``animations.js`` および
``actions.js`` を組み込むよう指示を受けます。
このような静的なメディア定義は、実行時に ``media`` というウィジェットのプロ
パティに変換されます。 ``CalendarWidget`` インスタンスのメディア情報は、プ
ロパティを介して以下のように取り出せます::
>>> w = CalendarWidget()
>>> print w.media
``Media`` には、以下のようなオプションがあります。必須のものはありません。
``css``
~~~~~~~
出力媒体ごとの CSS ファイル要件を定義した辞書です。
この辞書の値はメディアファイル名を列挙したタプルまたはリストです。メディア
ファイルのパスを指定する方法は `メディアパスの節
`_ を参照してください。
キーは出力媒体の名前です。このキーは、 CSS ファイルのメディアタイプ宣言で使
われるのと同じ、すなわち 'all', 'aural', 'braille', 'embossed',
'handheld', 'print', 'projection', 'screen', 'tty' そして 'tv' です。
メディアタイプごとに別々のスタイルシートを使いたければ、各メディアごとに
CSS ファイルのリストを指定してください。以下の例では、スクリーン (screen)
と印刷 (print) 用の CSS オプションを定義しています::
class Media:
css = {
'screen': ('pretty.css',),
'print': ('newspaper.css',)
}
一つの CSS ファイルグループが複数のメディアタイプをカバーできる場合、辞書の
キーにメディアタイプをカンマ区切りのリストにして指定してください。以下の例
では、テレビ (tv) およびプロジェクタ (projector) に同じメディアファイル要件
を指定しています::
class Media:
css = {
'screen': ('pretty.css',),
'tv,projector': ('lo_res.css',),
'print': ('newspaper.css',)
}
'print' の CSS 定義を使ってレンダリングを実行すると、以下のような HTML を出
力します::
``js``
~~~~~~
必要な JavaScript ファイルを列挙したタプルです。メディアファイルのパスを指
定する方法は `メディアパスの節`_ を参照してください。
``extend``
~~~~~~~~~~
メディア宣言を親クラスから継承するかどうかを決めるブール値です。
デフォルトの設定では、静的メディア宣言を使うオブジェクトは全て、親クラスの
ウィジェットで定義されているメディアを継承します。この継承は、親クラスでど
んなメディアファイル要件を宣言したかに関係なく行われます。例えば、上の例の
カレンダーウィジェットを、以下のように拡張したとします::
>>> class FancyCalendarWidget(CalendarWidget):
... class Media:
... css = {
... 'all': ('fancy.css',)
... }
... js = ('whizbang.js',)
>>> w = FancyCalendarWidget()
>>> print w.media
``FancyCalendarWidget`` は、親ウィジェットの全てのメディアを継承します。こ
のやり方で親モデルのメディア宣言を継承したくないのなら、 ``extend=False``
を ``Media`` クラスの宣言に追加します::
class FancyCalendar(Calendar):
class Media:
extend = False
css = {
'all': ('fancy.css',)
}
js = ('whizbang.js',)
>>> w = FancyCalendarWidget()
>>> print w.media
メディア宣言の継承をより細かく制御したければ、 `動的なプロパティ`_ を使って
メディア宣言を定義してください。動的プロパティを使えば、どのメディアファイ
ルを継承し、どのファイルを継承しないかを完全に制御できます。
.. _dynamic property: `Media as a dynamic property`_
.. _`動的なプロパティ`: `dynamic property`_
.. _`Media as a dynamic property`:
動的プロパティでのメディア定義
----------------------------------
``media`` プロパティを直接定義すれば、メディアファイル要件をより洗練された
やり方で制御できます。この方法では、 ``forms.Media`` のインスタンスを
返すようなプロパティを定義します。 ``forms.Media`` のコンストラクタは
``css`` や ``js`` といったキーワード引数をとり、それぞれ静的なメディア定義
で使われているのと同じ形式の値を指定できます。
例えば、先の ``CalendarWidget`` の静的メディア定義を動的なやり方で実現する
と、以下のように書けます::
class CalendarWidget(forms.TextInput):
def _media(self):
return forms.Media(css={'all': ('pretty.css',)},
js=('animations.js', 'actions.js'))
media = property(_media)
動的なメディアプロパティの戻り値を構築するための詳しい方法は、
`Media オブジェクト`_ の節を参照してください。
.. _form-media-paths:
.. _ref-forms-paths-in-media-definitions:
メディア定義のパス
--------------------
.. versionchanged:: 1.3
メディアファイルの場所を指定するためのパスは、相対でも絶対でもかまいません。
パスが ``'/'``, ``'http://'``, ``'https://'`` のいずれかで開始していれば、
絶対パス指定として、値をそのまま使います。それ以外のパスの前には適切な
プレフィクス(prefix)を指定します。
:doc:`staticfiles app ` の部分的な紹介として、
"静的なファイル"(イメージや CSS, JavaScript, など)に完全な Web ページを
表示するための二つの新しい設定が追加されました。:
:setting:`STATIC_URL` and :setting:`STATIC_ROOT` がそれです。
適切なプレフィクスを使うために、 Django は :setting:`STATIC_URL` が
``None`` ではないかどうかを確かめ、 :setting:`MEDIA_URL` を使うために自動的に
巻き戻ります。例えば、もしサイトの :setting:`MEDIA_URL` が
``'http://uploads.example.com/'`` であって :setting:`STATIC_URL` が ``None``
であった場合::
>>> class CalenderWidget(forms.TextInput):
class Media:
css = {
'all: ('/css/pretty.css',),
}
js = ('animations.js', 'http://othersite.com/actions.js')
>>> w = CalendarWidget()
>>> print w.media
ですが、もし :setting:`STATIC_URL` が ``'http://static.example.com/'`` なら
ば::
>>> w = CalendarWidget()
>>> print w.media
``Media`` オブジェクト
------------------------
ウィジェットやフォームの media 属性を調べると、 ``forms.Media`` オブジェク
トが返されます。すでに解説した通り、 ``Media`` オブジェクトの文字列表現は、
HTML ページの ```` ブロックでメディアファイルを取り込むときに必要な
HTML コードです。
とはいえ、 ``Media`` オブジェクトには他にもいくつか興味深いプロパティがあり
ます。
``Media`` サブセット
~~~~~~~~~~~~~~~~~~~~~~
特定のタイプのメディアファイル情報だけを出力したければ、添え字表記を使って
以下のように欲しいメディアだけを指定できます::
>>> w = CalendarWidget()
>>> print w.media
>>> print w.media['css']
添え字表記を使ってアクセスすると、新たな ``Media`` オブジェクトが返されます。
ただし、このオブジェクトには指定したメディアしか入っていません。
``Media`` オブジェクトを組み合わせる
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``Media`` オブジェクトは加算できます。二つの ``Media`` オブジェクトを加算す
ると、結果の ``Media`` オブジェクトには両方のメディアファイル情報が入ります::
>>> 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
フォームのメディア
---------------------
メディアファイル定義を持てるのはウィジェットだけではありません。フォームに
もまた、メディアを定義できます。フォームのメディアファイル定義には、ウィジェ
ットと同じ規則が適用されます。すなわち、静的・動的な宣言ができ、宣言内容に
は絶対・相対パスの解釈や継承といった規則がウィジェット同様に当てはまります。
どちらにメディア情報を定義したかに関係なく、フォームオブジェクトには
*必ず* ``media`` プロパティがあります。このプロパティのデフォルト値は、
フォームを構成する全てのウィジェットのメディアファイル定義を加算したもので
す::
>>> class ContactForm(forms.Form):
... date = DateField(widget=CalendarWidget)
... name = CharField(max_length=40, widget=OtherWidget)
>>> f = ContactForm()
>>> f.media
フォーム固有のメディア宣言を付加したい場合、例えば、フォームのレイアウトを
決める CSS を指定したい場合には、以下のようにメディア定義を追加します::
>>> 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