# 表單素材 ( `Media` 類)
渲染有吸引力的、易于使用的web表單不僅僅需要HTML -- 同時也需要CSS樣式表,并且,如果你打算使用奇妙的web2.0組件,你也需要在每個頁面包含一些JavaScript。任何提供的頁面都需要CSS和JavaScript的精確配合,它依賴于頁面上所使用的組件。
這就是素材定義所導入的位置。Django允許你將一些不同的文件 -- 像樣式表和腳本 -- 與需要這些素材的表單和組件相關聯。例如,如果你想要使用日歷來渲染DateField,你可以定義一個自定義的日歷組件。這個組件可以與渲染日歷所需的CSS和JavaScript關聯。當日歷組件用在表單上的時候,Django可以識別出所需的CSS和JavaScript文件,并且提供一個文件名的列表,以便在你的web頁面上簡單地包含這些文件。
素材和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="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的字典。
字典中的值應該為文件名稱的列表或者元組。對于如何指定這些文件的路徑,詳見[_路徑的章節_](#form-asset-paths)。
字典中的鍵位輸出媒體的類型。它們和媒體聲明中CSS文件接受的類型相同: ‘all’, ‘aural’, ‘braille’, ‘embossed’, ‘handheld’, ‘print’, ‘projection’, ‘screen’, ‘tty’ 和‘tv’。如果你需要為不同的媒體類型使用不同的樣式表,要為每個輸出媒體提供一個CSS文件的列表。下面的例子提供了兩個CSS選項 -- 一個用于屏幕,另一個用于打印:
```
class Media:
css = {
'screen': ('pretty.css',),
'print': ('newspaper.css',)
}
```
如果一組CSS文件適用于多種輸出媒體的類型,字典的鍵可以為輸出媒體類型的逗號分隔的列表。在下面的例子中,TV和投影儀具有相同的媒體需求:
```
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文件由一個元組來描述。如何制定這些文件的路徑,詳見[_路徑一節_](#form-asset-paths)。
### 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>
```
如果你需要對繼承進行更多控制,要使用[_動態屬性_](#dynamic-property)來定義你的素材。動態屬性可以提供更多的控制,來控制繼承哪個文件。
## Media as a dynamic property
如果你需要對素材需求進行更多的復雜操作,你可以直接定義`media`屬性。這可以通過定義一個返回`forms.Media`實例的組件屬性來實現。`forms.Media`的構造器接受 `css` 和 `js`關鍵字參數,和在靜態媒體定義中的格式相同。
例如,我們的日歷組件的靜態定義可以定義成動態形式:
```
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://`開頭,會被解釋為絕對路徑。所有其它的路徑會在開頭追加合適前綴的值。
作為 [_staticfiles app_](../../ref/contrib/staticfiles.html)的簡介的一部分,添加了兩個新的設置,它們涉及到渲染完整頁面所需的“靜態文件”:[`STATIC_URL`](../../ref/settings.html#std:setting-STATIC_URL) 和[`STATIC_ROOT`](../../ref/settings.html#std:setting-STATIC_ROOT)。
Django 會檢查是否[`STATIC_URL`](../../ref/settings.html#std:setting-STATIC_URL)設置不是`None`,來尋找合適的前綴來使用,并且會自動回退使用[`MEDIA_URL`](../../ref/settings.html#std:setting-MEDIA_URL)。例如,如果你站點的 [`MEDIA_URL`](../../ref/settings.html#std:setting-MEDIA_URL) 是 `'http://uploads.example.com/'` 并且 [`STATIC_URL`](../../ref/settings.html#std: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>
```
但如果[`STATIC_URL`](../../ref/settings.html#std: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>
```
## `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`, _所有_表單對象都有`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>
```
> 譯者:[Django 文檔協作翻譯小組](http://python.usyiyi.cn/django/index.html),原文:[Integrating media](https://docs.djangoproject.com/en/1.8/topics/forms/media/)。
>
> 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 協議發布,轉載請保留作者署名和文章出處。
>
> [Django 文檔協作翻譯小組](http://python.usyiyi.cn/django/index.html)人手緊缺,有興趣的朋友可以加入我們,完全公益性質。交流群:467338606。
- 新手入門
- 從零開始
- 概覽
- 安裝
- 教程
- 第1部分:模型
- 第2部分:管理站點
- 第3部分:視圖和模板
- 第4部分:表單和通用視圖
- 第5部分:測試
- 第6部分:靜態文件
- 高級教程
- 如何編寫可重用的應用
- 為Django編寫首個補丁
- 模型層
- 模型
- 模型語法
- 元選項
- 模型類
- 查詢集
- 執行查詢
- 查找表達式
- 模型的實例
- 實例方法
- 訪問關聯對象
- 遷移
- 模式編輯器
- 編寫遷移
- 高級
- 管理器
- 原始的SQL查詢
- 聚合
- 多數據庫
- 自定義查找
- 條件表達式
- 數據庫函數
- 其它
- 遺留的數據庫
- 提供初始數據
- 優化數據庫訪問
- 視圖層
- 基礎
- URL配置
- 視圖函數
- 快捷函數
- 裝飾器
- 參考
- 內建的視圖
- TemplateResponse 對象
- 文件上傳
- 概覽
- File 對象
- 儲存API
- 管理文件
- 自定義存儲
- 基于類的視圖
- 概覽
- 內建顯示視圖
- 內建編輯視圖
- API參考
- 分類索引
- 高級
- 生成 CSV
- 生成 PDF
- 中間件
- 概覽
- 內建的中間件類
- 模板層
- 基礎
- 面向設計師
- 語言概覽
- 人性化
- 面向程序員
- 表單
- 基礎
- 概覽
- 表單API
- 內建的Widget
- 高級
- 整合媒體
- 開發過程
- 設置
- 概覽
- 應用程序
- 異常
- 概覽
- django-admin 和 manage.py
- 添加自定義的命令
- 測試
- 介紹
- 部署
- 概述
- WSGI服務器
- 部署靜態文件
- 通過email追蹤代碼錯誤
- Admin
- 管理操作
- 管理文檔生成器
- 安全
- 安全概述
- 說明Django中的安全問題
- 點擊劫持保護
- 加密簽名
- 國際化和本地化
- 概述
- 本地化WEB UI格式化輸入
- “本地特色”
- 常見的網站應用工具
- 認證
- 概覽
- 使用認證系統
- 密碼管理
- 日志
- 分頁
- 會話
- 數據驗證
- 其它核心功能
- 按需內容處理
- 重定向
- 信號
- 系統檢查框架