# Django 5.2.8 documentation

# 表单资源（ `Media` 类）

呈现一个有吸引力的、易于使用的网络表格需要的不仅仅是 HTML，它还需要 CSS 样式表，如果你想使用花哨的部件，你可能还需要在每个页面上包含一些 JavaScript。任何特定页面所需的 CSS 和 JavaScript 的确切组合将取决于该页面上使用的部件。

这就是资源定义的作用。Django 允许你将不同的文件——如样式表和脚本——与需要这些资产的表单和部件联系起来。例如，如果你想用一个日历来渲染 DateFields，你可以定义一个自定义的日历部件。然后这个部件可以与渲染日历所需的 CSS 和 JavaScript 相关联。当日历部件在表单上使用时，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` 。每次CalendarWidget在表单上使用时，该表单都会包含CSS文件 `pretty.css` ，以及JavaScript文件 `animations.js` 和 `actions.js` 。

这个静态定义在运行时被转换为一个名为 `media` 的小部件属性。可以通过这个属性获取 `CalendarWidget` 实例的资产列表：

    >>> w = CalendarWidget()
    >>> print(w.media)
    <link href="https://static.example.com/pretty.css" media="all" rel="stylesheet">
    <script src="https://static.example.com/animations.js"></script>
    <script src="https://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"],
        }

如果这个最后的 CSS 定义要被渲染，它将变成以下 HTML：

    <link href="https://static.example.com/pretty.css" media="screen" rel="stylesheet">
    <link href="https://static.example.com/lo_res.css" media="tv,projector" rel="stylesheet">
    <link href="https://static.example.com/newspaper.css" media="print" rel="stylesheet">

### `js`

描述所需JavaScript文件的一个元组。有关如何指定这些文件的路径的详细内容，请参阅 路径章节 。

#### `Script` objects

New in Django 5.2.

class Script(src, **attributes)

  Represents a script file.

  The first parameter, `src`, is the string path to the script file. See the section on paths for details on how to specify paths to these files.

  The optional keyword arguments, `**attributes`, are HTML attributes that are set on the rendered `<script>` tag.

  See 路径作为对象 for usage examples.

### `extend`

定义了 `Media` 声明继承行为的一个布尔值。

默认情况下，使用静态 `Media` 定义的任何对象都会继承与父小部件关联的所有资产。这将发生无论父级如何定义自己的要求。例如，如果我们要扩展上面示例中的基本日历小部件：

    >>> class FancyCalendarWidget(CalendarWidget):
    ...     class Media:
    ...         css = {
    ...             "all": ["fancy.css"],
    ...         }
    ...         js = ["whizbang.js"]
    ...

    >>> w = FancyCalendarWidget()
    >>> print(w.media)
    <link href="https://static.example.com/pretty.css" media="all" rel="stylesheet">
    <link href="https://static.example.com/fancy.css" media="all" rel="stylesheet">
    <script src="https://static.example.com/animations.js"></script>
    <script src="https://static.example.com/actions.js"></script>
    <script src="https://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="https://static.example.com/fancy.css" media="all" rel="stylesheet">
    <script src="https://static.example.com/whizbang.js"></script>

如果您需要更多的继承控制，用一个 动态属性 定义你的 。动态属性使您可以完全控制哪些文件是否继承。

## 把 `Media` 作为动态属性

If you need to perform some more sophisticated manipulation of asset requirements, you can define the `media` property directly. This is done by defining a widget property that returns an instance of `forms.Media`. The constructor for `forms.Media` accepts `css` and `js` keyword arguments in the same format as that used in a static media definition.

例如，我们也可以以动态的方式定义日历组件的静态定义：

    class CalendarWidget(forms.TextInput):
        @property
        def media(self):
            return forms.Media(
                css={"all": ["pretty.css"]}, js=["animations.js", "actions.js"]
            )

更多有关如何为动态 `media` 属性构建返回值的内容，请参阅 媒体对象 章节。

## 资源定义中的路径

### 路径作为字符串

用于指定资产的字符串路径可以是相对路径或绝对路径。如果路径以 `/`、`http://` 或 `https://` 开头，它将被解释为绝对路径，并保持不变。所有其他路径都将以适当前缀的值作为前缀。如果安装了 `django.contrib.staticfiles` 应用程序，它将用于提供资产。

无论您是否使用 `django.contrib.staticfiles` ，都需要设置 `STATIC_URL` 和 `STATIC_ROOT` 来渲染一张完整的网页。

为了找到要使用的前缀，Django 会检查 `STATIC_URL` 设置是否不为 `None`，并自动回退到使用 `MEDIA_URL`。例如，如果你的站点的 `MEDIA_URL` 是 `'https://uploads.example.com/'` 而 `STATIC_URL` 是 `None`：

    >>> from django import forms
    >>> class CalendarWidget(forms.TextInput):
    ...     class Media:
    ...         css = {
    ...             "all": ["/css/pretty.css"],
    ...         }
    ...         js = ["animations.js", "https://othersite.com/actions.js"]
    ...

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

但如果 `STATIC_URL` 是 `'https://static.example.com/'`：

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

或者如果使用 `ManifestStaticFilesStorage` 配置了 `staticfiles`：

    >>> 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="https://othersite.com/actions.js"></script>

### 路径作为对象

Assets may also be object-based, using `Script`. Furthermore, these allow you to pass custom HTML attributes:

    class Media:
        js = [
            Script(
                "https://cdn.example.com/something.min.js",
                **{
                    "crossorigin": "anonymous",
                    "async": True,
                },
            ),
        ]

If this Media definition were to be rendered, it would become the following HTML:

    <script src="https://cdn.example.com/something.min.js"
            crossorigin="anonymous"
            async>
    </script>

Changed in Django 5.2:

The object class `Script` was added.

## `Media` 对象

当您访问表单或者组件的 `media` 属性时，返回值是一个 `forms.Media` 对象。正如我们已经看到的， `Media` 对象的字符串表示是一段需要在您HTML页面的 `<head>` 块中包含相关文件的HTML代码。

然而， `Media` 对象还有其他一些有趣的属性。

### 资源的子集

如果你只想要特定类型的文件，你可以使用下标运算符来过滤出感兴趣的媒体。例如：

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

    >>> print(w.media["css"])
    <link href="https://static.example.com/pretty.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="https://static.example.com/pretty.css" media="all" rel="stylesheet">
    <script src="https://static.example.com/animations.js"></script>
    <script src="https://static.example.com/actions.js"></script>
    <script src="https://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="https://static.example.com/jQuery.js"></script>
    <script src="https://static.example.com/calendar.js"></script>
    <script src="https://static.example.com/time.js"></script>
    <script src="https://static.example.com/noConflict.js"></script>

合并 `Media` 对象时，如果资源排序冲突，会导致警告提示： `MediaOrderConflictWarning` 。

## 表单上的 `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="https://static.example.com/pretty.css" media="all" rel="stylesheet">
    <script src="https://static.example.com/animations.js"></script>
    <script src="https://static.example.com/actions.js"></script>
    <script src="https://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="https://static.example.com/pretty.css" media="all" rel="stylesheet">
    <link href="https://static.example.com/layout.css" media="all" rel="stylesheet">
    <script src="https://static.example.com/animations.js"></script>
    <script src="https://static.example.com/actions.js"></script>
    <script src="https://static.example.com/whizbang.js"></script>