# Django 5.0.9.dev20240806133106 ドキュメント

# フォームアセット (`Media` クラス)

魅力的で使いやすいウェブのフォームを作るには、HTML だけでは不十分です。CSS スタイルシートも必要です。さらに豪華なウィジェットを使いたければ、JavaScript を各ページに配置する必要もあるでしょう。あるページに必要な CSS と JavaScript の組み合わせは、そのページに使用されるウィジェットによって異なります。

ここでアセット定義の出番です。Django では異なるファイル（例えばスタイルシートとスクリプト）を、これらを必要とするフォームおよびウィジェットと結びつけることができます。例として、DateFields を描画するのにカレンダーを使いたい場合、独自の Calendar ウィジェットを定義したとき、このウィジェットは CSS や JavaScript と紐付けることができます。フォーム上でこの Calendar ウィジェットを使用するとき、Django は必要な CSS および JavaScript のファイルを特定し、Web ページ上で参照できるよう、フォーム内にファイル名のリストを生成します。

アセットと 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` に基づいています。CalendarWidget をフォーム上で使用すると、CSS ファイル `pretty.css` と JavaScript ファイル `animations.js` および `actions.js` を読み込めるようになります。

この静的定義は実行時に `media` というウィジェットプロパティに変換されます。 `CalendarWidget` インスタンスのアセットリストは、このプロパティから取得できます:

    >>> w = CalendarWidget()
    >>> print(w.media)
    <link href="http://static.example.com/pretty.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 ファイルを作成してください。以下の例は 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" media="screen" rel="stylesheet">
    <link href="http://static.example.com/lo_res.css" media="tv,projector" rel="stylesheet">
    <link href="http://static.example.com/newspaper.css" media="print" rel="stylesheet">

### `js`

必要な JavaScript ファイルを定義したタプルです。ファイルのパスを指定する方法は パスの章 を参照してください。

### `extend`

`Media` 宣言に対する継承動作を定義する真偽値です。

デフォルトでは、静的な `Media` 定義を使用しているオブジェクトは、親ウィジェットに関連するすべてのアセットを継承します。これは、親ウィジェットが自身の要件をどのように定義しているかによりません。例えば、上の例の基本的なカレンダーウィジェットを拡張するとします:

    >>> 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" media="all" rel="stylesheet">
    <link href="http://static.example.com/fancy.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>

FancyCalendar ウィジェットは親ウィジェットからすべてのアセットを継承します。このように `Media` を継承させたくない場合は、`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" media="all" rel="stylesheet">
    <script src="http://static.example.com/whizbang.js"></script>

継承をさらにコントロールしたい場合は、 動的なプロパティ を使ってアセットを定義してください。動的なプロパティはどのファイルを継承し、どのファイルを継承しないかを完全にコントロールできます。

## 動的プロパティとしての `Media`

アセット要件のより詳細な操作が必要な場合、`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 を参照してください。

## アセット定義内のパス

### 文字列のパス

アセットを指定する文字列パスは相対パスと絶対パスがあります。パスが `/`, `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" media="all" rel="stylesheet">
    <script src="http://uploads.example.com/animations.js"></script>
    <script src="http://othersite.com/actions.js"></script>

しかし、 `STATIC_URL` が `'http://static.example.com/'` の場合は下記のようになります:

    >>> w = CalendarWidget()
    >>> print(w.media)
    <link href="/css/pretty.css" media="all" rel="stylesheet">
    <script src="http://static.example.com/animations.js"></script>
    <script src="http://othersite.com/actions.js"></script>

または `staticfiles` が `ManifestStaticFilesStorage` を使って設定されている場合、下記のようになります:

    >>> w = CalendarWidget()
    >>> print(w.media)
    <link href="/css/pretty.css" media="all" rel="stylesheet">
    <script src="https://static.example.com/animations.27e20196a850.js"></script>
    <script src="http://othersite.com/actions.js"></script>

### オブジェクトとしてのパス

アセットパスは `__html__()` メソッドを実装したハッシュ可能なオブジェクトとして指定することもできます。 `html_safe()` デコレータを使って `__html__()` メソッドを追加します。このオブジェクトは HTML の `<script>` や `<link>` タグの内容を出力します:

    >>> from django import forms
    >>> from django.utils.html import html_safe
    >>>
    >>> @html_safe
    ... class JSPath:
    ...     def __str__(self):
    ...         return '<script src="https://example.org/asset.js" defer>'
    ...

    >>> class SomeWidget(forms.TextInput):
    ...     class Media:
    ...         js = [JSPath()]
    ...

## `Media` objects

ウィジェットやフォームの `media` 属性に応答指令信号を送ると、`forms.Media` オブジェクトが戻り値となります。すでに見たように、`Media` オブジェクトの文字列表現は HTMLで、HTML ページ内の `<head>` ブロックに関連ファイルを含める必要があります。

ただし、`Media` オブジェクトにはいくつかの面白いプロパティが存在します。

### アセットのサブセット

特定のタイプのファイルだけが必要な場合は、添え字を使って、欲しいメディアだけをフィルタリングできます。たとえば以下のようにします:

    >>> w = CalendarWidget()
    >>> print(w.media)
    <link href="http://static.example.com/pretty.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" media="all" rel="stylesheet">

添え字演算子を使うと、返される値は、指定したメディアだけを含む新しい `Media` オブジェクトです。

### `Media` オブジェクトを結合する

`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" 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` オブジェクトは、各 `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` 宣言を定義しているかどうかに関わらず、すべてのフォームオブジェクトは `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" 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>

フォームレイアウト用の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" media="all" rel="stylesheet">
    <link href="http://static.example.com/layout.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>