表单资产(媒体类) — Django 文档

来自菜鸟教程
Django/docs/2.2.x/topics/forms/media
跳转至:导航、​搜索

表单资产(Media 类)

呈现一个有吸引力且易于使用的 Web 表单需要的不仅仅是 HTML - 它还需要 CSS 样式表,如果您想使用花哨的“Web2.0”小部件,您可能还需要在每个页面上包含一些 JavaScript。 任何给定页面所需的 CSS 和 JavaScript 的确切组合将取决于该页面上使用的小部件。

这就是资产定义的用武之地。 Django 允许您将不同的文件(如样式表和脚本)与需要这些资产的表单和小部件相关联。 例如,如果您想使用日历来呈现 DateFields,您可以定义一个自定义 Calendar 小部件。 然后可以将此小部件与呈现日历所需的 CSS 和 JavaScript 相关联。 在表单上使用 Calendar 小部件时,Django 能够识别所需的 CSS 和 JavaScript 文件,并以适合在 Web 页面中轻松包含的表单提供文件名列表。

资产和 Django 管理员

Django Admin 应用程序为日历、过滤选择等定义了许多自定义小部件。 这些小部件定义资产要求,Django Admin 使用自定义小部件代替 Django 默认值。 管理模板将仅包含在任何给定页面上呈现小部件所需的那些文件。

如果您喜欢 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.jsactions.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 选项的列表。 没有必需的选项。

css

描述各种形式的输出媒体所需的 CSS 文件的字典。

字典中的值应该是文件名的元组/列表。 有关如何指定这些文件的路径的详细信息,请参阅 路径 部分。

字典中的键是输出媒体类型。 这些是媒体声明中 CSS 文件接受的相同类型:“all”、“aural”、“braille”、“embossed”、“handheld”、“print”、“projection”、“screen”、“tty”和“电视'。 如果您需要为不同的媒体类型使用不同的样式表,请为每个输出媒体提供一个 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="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 声明的定义继承行为的布尔值。

默认情况下,任何使用静态 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" 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 以这种方式被继承,则在 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>

如果您需要对继承进行更多控制,请使用 动态属性 定义您的资产。 动态属性使您可以完全控制继承哪些文件,哪些不继承。


Media 作为动态属性

如果您需要对资产要求执行一些更复杂的操作,您可以直接定义 media 属性。 这是通过定义一个返回 forms.Media 实例的小部件属性来完成的。 forms.Media 的构造函数接受 cssjs 关键字参数,其格式与静态媒体定义中使用的格式相同。

例如,日历小部件的静态定义也可以以动态方式定义:

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,都需要 :setting:`STATIC_URL`:setting:`STATIC_ROOT` 设置来渲染一个完整的网页。

为了找到要使用的适当前缀,Django 将检查 :setting:`STATIC_URL` 设置是否不是 None 并自动回退到使用 :setting:`MEDIA_URL`[ X181X]。 例如,如果您网站的 :setting:`MEDIA_URL`'http://uploads.example.com/' 并且 :setting:`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>

但是如果 :setting:`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 物体

当您查询小部件或表单的 media 属性时,返回的值是一个 forms.Media 对象。 正如我们已经看到的,Media 对象的字符串表示是在 HTML 页面的 <head> 块中包含相关文件所需的 HTML。

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

资产子集

如果您只需要特定类型的文件,您可以使用下标运算符来过滤掉感兴趣的媒体。 例如:

>>> 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 对象

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="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>

资产顺序

资产插入 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


表格上的 Media

小部件不是唯一可以具有 media 定义的对象——表单也可以定义 media。 表单上 media 定义的规则与小部件的规则相同:声明可以是静态的或动态的; 这些声明的路径和继承规则完全相同。

无论您是否定义了 media 声明,all 表单对象都具有 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>

如果您想将其他资产与表单相关联——例如,用于表单布局的 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" 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>