Skip to main content

表单资产(Media 类)

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

这就是资产定义的地方。Django允许您将不同的文件(如样式表和脚本)与需要这些资产的窗体和窗口小部件相关联。例如,如果要使用日历来渲染DateField,您可以定义自定义日历窗口小部件。然后,此窗口小部件可以与呈现日历所需的CSS和JavaScript相关联。当在窗体上使用日历小部件时,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')

这段代码定义了一个基于 TextInputCalendarWidget。每次在表单上使用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 的实例的widget属性来完成。 forms.Media 的构造函数以与静态媒体定义中使用的格式相同的格式接受 cssjs 关键字参数。

例如,我们的Calendar Widget的静态定义也可以动态定义:

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

有关如何为动态 media 属性构造返回值的更多详细信息,请参阅 Media objects 部分。

资产定义中的路径

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

无论您是否使用 django.contrib.staticfiles,都需要 STATIC_URLSTATIC_ROOT 设置才能呈现完整的网页。

要找到要使用的适当的前缀,Django将检查 STATIC_URL 设置是否不是 None,并自动回退到使用 MEDIA_URL。例如,如果您的网站的 MEDIA_URL'http://uploads.example.com/'STATIC_URLNone:

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

或者如果使用 ~django.contib.staticfiles.ManifestStaticFilesStorage 配置 staticfiles:

>>> 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>
Changed in Django 1.10:

旧版本未使用 django.contrib.staticfiles 提供资产。

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>

Media 表格

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

无论是否定义 media 声明,all 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>

如果要将其他资源与表单关联 - 例如,表单布局的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>