表单 Assets Media 类)

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

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

资产和Django管理

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

如果您喜欢django管理应用程序使用的小部件,请随意在您自己的应用程序中使用它们!它们都储存在 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="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文件的字典。

字典中的值应该是文件名的元组/列表。见 the section on paths 有关如何指定这些文件的路径的详细信息。

字典中的键是输出媒体类型。这些类型与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文件的元组。见 the section on paths 有关如何指定这些文件的路径的详细信息。

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 要以这种方式继承,请添加一个 extend=False 声明提交给 Media 声明:

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

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

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 objects 有关如何构造动态的返回值的详细信息 media 性质。

资产定义中的路径

字符串形式的路径

用于指定资源的字符串路径可以是相对路径,也可以是绝对路径。如果路径以 /http://https:// ,它将被解释为绝对路径,并按原样保留。所有其他路径都将使用相应前缀的值作为前缀。如果 django.contrib.staticfiles 应用程序已安装,它将用于服务资产。

是否使用 django.contrib.staticfiles , the STATIC_URLSTATIC_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>

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

将路径作为对象

资源路径也可以作为可散列对象提供,实现 __html__() 方法。这个 __html__() 方法通常使用 html_safe() 装饰师。该对象负责输出完整的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" rel="stylesheet">'
...

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

Media 对象

当你审问 media 小部件或窗体的属性,返回的值是 forms.Media 对象。正如我们已经看到的,字符串表示 Media 对象是将相关文件包含在 <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 窗体上的定义与小部件的规则相同:声明可以是静态的或动态的;这些声明的路径和继承规则完全相同。

不管您是否定义了 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="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>