渲染有吸引力的、易于使用的web表單不僅僅需要HTML -- 同時(shí)也需要CSS樣式表,并且,如果你打算使用奇妙的web2.0組件,你也需要在每個(gè)頁(yè)面包含一些JavaScript。任何提供的頁(yè)面都需要CSS和JavaScript的精確配合,它依賴(lài)于頁(yè)面上所使用的組件。
這就是素材定義所導(dǎo)入的位置。Django允許你將一些不同的文件 -- 像樣式表和腳本 -- 與需要這些素材的表單和組件相關(guān)聯(lián)。例如,如果你想要使用日歷來(lái)渲染DateField,你可以定義一個(gè)自定義的日歷組件。這個(gè)組件可以與渲染日歷所需的CSS和JavaScript關(guān)聯(lián)。當(dāng)日歷組件用在表單上的時(shí)候,Django可以識(shí)別出所需的CSS和JavaScript文件,并且提供一個(gè)文件名的列表,以便在你的web頁(yè)面上簡(jiǎn)單地包含這些文件。
素材和Django Admin
Django的Admin應(yīng)用為日歷、過(guò)濾選擇等一些東西定義了一些自定義的組件。這些組件定義了素材的需求,DJango Admin使用這些自定義組件來(lái)代替Django默認(rèn)的組件。Admin模板只包含在提供頁(yè)面上渲染組件所需的那些文件。
如果你喜歡Django Admin應(yīng)用所使用的那些組件,可以在你的應(yīng)用中隨意使用它們。它們位于django.contrib.admin.widgets。
選擇哪個(gè)JavaScript工具包?
現(xiàn)在有許多JavaScript工具包,它們中許多都包含組件(比如日歷組件),可以用于提升你的應(yīng)用。Django 有意避免去稱(chēng)贊任何一個(gè)JavaScript工具包。每個(gè)工具包都有自己的有點(diǎn)和缺點(diǎn) -- 要使用適合你需求的任何一個(gè)。Django 有能力集成任何JavaScript工具包。
定義素材的最簡(jiǎn)單方式是作為靜態(tài)定義。如果使用這種方式,定義在Media內(nèi)部類(lèi)中出現(xiàn),內(nèi)部類(lèi)的屬性定義了需求。
這是一個(gè)簡(jiǎn)單的例子:
from django import forms
class CalendarWidget(forms.TextInput):
class Media:
css = {
'all': ('pretty.css',)
}
js = ('animations.js', 'actions.js')
上面的代碼定義了 CalendarWidget,它繼承于TextInput。每次CalendarWidget在表單上使用時(shí),表單都會(huì)包含CSS文件pretty.css,以及JavaScript文件animations.js 和 actions.js。
靜態(tài)定義在運(yùn)行時(shí)被轉(zhuǎn)換為名為media的組件屬性。CalendarWidget實(shí)例的素材列表可以通過(guò)這種方式獲?。?/p>
>>> w = CalendarWidget()
>>> print(w.media)
<link 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選項(xiàng)的列表。它們之中沒(méi)有必需選項(xiàng)。
各種表單和輸出媒體所需的,描述CSS的字典。
字典中的值應(yīng)該為文件名稱(chēng)的列表或者元組。對(duì)于如何指定這些文件的路徑,詳見(jiàn)路徑的章節(jié)。
字典中的鍵位輸出媒體的類(lèi)型。它們和媒體聲明中CSS文件接受的類(lèi)型相同: ‘a(chǎn)ll’, ‘a(chǎn)ural’, ‘braille’, ‘embossed’, ‘handheld’, ‘print’, ‘projection’, ‘screen’, ‘tty’ 和‘tv’。如果你需要為不同的媒體類(lèi)型使用不同的樣式表,要為每個(gè)輸出媒體提供一個(gè)CSS文件的列表。下面的例子提供了兩個(gè)CSS選項(xiàng) -- 一個(gè)用于屏幕,另一個(gè)用于打印:
class Media:
css = {
'screen': ('pretty.css',),
'print': ('newspaper.css',)
}
如果一組CSS文件適用于多種輸出媒體的類(lèi)型,字典的鍵可以為輸出媒體類(lèi)型的逗號(hào)分隔的列表。在下面的例子中,TV和投影儀具有相同的媒體需求:
class Media:
css = {
'screen': ('pretty.css',),
'tv,projector': ('lo_res.css',),
'print': ('newspaper.css',)
}
如果最后的CSS定義即將被渲染,會(huì)變成下面的HTML:
<link type="text/css" media="screen" rel="stylesheet" />
<link type="text/css" media="tv,projector" rel="stylesheet" />
<link type="text/css" media="print" rel="stylesheet" />
所需的JavaScript文件由一個(gè)元組來(lái)描述。如何制定這些文件的路徑,詳見(jiàn)路徑一節(jié)。
一直布爾值,定義了Media聲明的繼承行為。
通常,任何使用靜態(tài)Media定義的對(duì)象都會(huì)繼承所有和父組件相關(guān)的素材。無(wú)論父對(duì)象如何定義它自己的需求,都是這樣。例如,如果我們打算從上面的例子中擴(kuò)展我們的基礎(chǔ)日歷控件:
>>> class FancyCalendarWidget(CalendarWidget):
... class Media:
... css = {
... 'all': ('fancy.css',)
... }
... js = ('whizbang.js',)
>>> w = FancyCalendarWidget()
>>> print(w.media)
<link type="text/css" media="all" rel="stylesheet" />
<link 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 type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="http://static.example.com/whizbang.js"></script>
如果你需要對(duì)繼承進(jìn)行更多控制,要使用動(dòng)態(tài)屬性來(lái)定義你的素材。動(dòng)態(tài)屬性可以提供更多的控制,來(lái)控制繼承哪個(gè)文件。
如果你需要對(duì)素材需求進(jìn)行更多的復(fù)雜操作,你可以直接定義media屬性。這可以通過(guò)定義一個(gè)返回forms.Media實(shí)例的組件屬性來(lái)實(shí)現(xiàn)。forms.Media的構(gòu)造器接受 css 和 js關(guān)鍵字參數(shù),和在靜態(tài)媒體定義中的格式相同。
例如,我們的日歷組件的靜態(tài)定義可以定義成動(dòng)態(tài)形式:
class CalendarWidget(forms.TextInput):
def _media(self):
return forms.Media(css={'all': ('pretty.css',)},
js=('animations.js', 'actions.js'))
media = property(_media)
對(duì)于如何構(gòu)建動(dòng)態(tài)media 屬性的的返回值,詳見(jiàn)媒體對(duì)象一節(jié)。
用于指定素材的路徑可以是相對(duì)的或者絕對(duì)的。如果路徑以 /,http:// 或者https://開(kāi)頭,會(huì)被解釋為絕對(duì)路徑。所有其它的路徑會(huì)在開(kāi)頭追加合適前綴的值。
作為 staticfiles app的簡(jiǎn)介的一部分,添加了兩個(gè)新的設(shè)置,它們涉及到渲染完整頁(yè)面所需的“靜態(tài)文件”:STATIC_URL 和STATIC_ROOT。
Django 會(huì)檢查是否STATIC_URL設(shè)置不是None,來(lái)尋找合適的前綴來(lái)使用,并且會(huì)自動(dòng)回退使用MEDIA_URL。例如,如果你站點(diǎn)的 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" 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>
Media對(duì)象當(dāng)你訪問(wèn)表單上的一個(gè)組件的media屬性時(shí),返回值是一個(gè)forms.Media對(duì)象。就像已經(jīng)看到的那樣,表示 Media 對(duì)象的字符串,是在你的HTML頁(yè)面的<head> 代碼段包含相關(guān)文件所需的HTML。
然而,Media對(duì)象具有一些其它的有趣屬性。
如果你僅僅想得到特定類(lèi)型的文件,你可以使用下標(biāo)運(yùn)算符來(lái)過(guò)濾出你感興趣的媒體。例如:
>>> w = CalendarWidget()
>>> print(w.media)
<link 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 type="text/css" media="all" rel="stylesheet" />
當(dāng)你使用下標(biāo)運(yùn)算符的時(shí)候,返回值是一個(gè)新的 Media對(duì)象,但是只含有感興趣的媒體。
Media對(duì)象Media 對(duì)象可以添加到一起。添加兩個(gè)Media的時(shí)候,產(chǎn)生的Media對(duì)象含有二者指定的素材的并集:
>>> 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 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定義的對(duì)象 -- 表單可以定義media。在表單上定義media 的規(guī)則和組件上面一樣:定義可以為靜態(tài)的或者動(dòng)態(tài)的。聲明的路徑和繼承規(guī)則也嚴(yán)格一致。
無(wú)論是否你定義了media, _所有_表單對(duì)象都有media屬性。這個(gè)屬性的默認(rèn)值是,向所有屬于這個(gè)表單的組件添加media定義的結(jié)果。
>>> from django import forms
>>> class ContactForm(forms.Form):
... date = DateField(widget=CalendarWidget)
... name = CharField(max_length=40, widget=OtherWidget)
>>> f = ContactForm()
>>> f.media
<link 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>
如果你打算向表單關(guān)聯(lián)一些額外的素材 -- 例如,表單布局的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 type="text/css" media="all" rel="stylesheet" />
<link 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>
譯者:Django 文檔協(xié)作翻譯小組,原文:Integrating media。
本文以 CC BY-NC-SA 3.0 協(xié)議發(fā)布,轉(zhuǎn)載請(qǐng)保留作者署名和文章出處。
Django 文檔協(xié)作翻譯小組人手緊缺,有興趣的朋友可以加入我們,完全公益性質(zhì)。交流群:467338606。