第六章-Admin接口
**************
這一章,我們會討論以下話題:
- 定制admin
- 增強admin的模型
- Admin的最佳實踐
- 特性標識
Django被談論到最多的是, 與其他的競爭對手相比它將admin接口獨立了出來。。admin接口是一個自動地生成添加和修改一個站點內容的用戶接口。不僅如此,admin也是Django的殺手級應用,它使項目中對模型生成admin接口的乏味的任務可以自動化。
admin能夠讓你的團隊在同一時間內添加內容,不間斷開發。只要模型已經應用了遷移,你僅需添加一行或者兩行代碼就可以生成模型的的admin接口。
## 使用admin接口
在Django1.7中,admin接口默認是啟用的。在創建項目之后,你瀏覽`http://127.0.0.1:800/admin`時能夠看到一個登錄頁面。
如果輸入超級用戶憑證(或者任意站點注冊用戶的憑證),那么你會登錄到admin中,一如下面截圖所示:
不過,模型在admin管理界面是不可見的,除非你定義一個與之對應的`ModelAdmin`類。這個操作通常定義在應用的admin.py文件,一如下面所示:
```python
from django.contrib import admin
from . import models
admin.site.register(models.SuperHero)
```
此處,`ModelAdmin`的到注冊器的第二個參數被省略。因此,我們會獲得一個默認的Post模型的admin接口。讓我們看看如何創建并自定義``ModelAdmin`類。
>### 注釋
**引路人**
“還有咖啡嗎???“一個聲音來自備餐室角落的聲音問道。蘇差點兒把咖啡灑了出來。她前面站著一位身著緊身紅藍相間衣服?,面帶微笑,將手叉在腰間的高個子男人。
>"Oh, my god," said Sue as she wiped the coffee stain with a napkin. "Sorry, I think I scared you," said Captain Obvious "What is the emergency?"
"Isn't it obvious that she doesn't know?" said a calm feminine voice from above. Sue looked up to find a shadowy figure slowly descend from the open hall. Her face was partially obscured by her dark matted hair that had a few grey streaks. "Hi Hexa!" said the Captain "But then, what was the message on SuperBook about?"
>“哎呦喂,”蘇說道,同時另一邊她小毛巾擦掉了潑出去的咖啡。“不好意啊,嚇到你了,”裝傻隊長問道。“什么事這么急啊?”
>Soon, they were all at Steve's office staring at his screen. "See, I told you there is no beacon on the front page," said Evan. "We are still developing that feature." "Wait," said Steve. "Let me login through a non-staff account."
In a few seconds, the page refreshed and an animated red beacon prominently appeared at the top. "That's the beacon I was talking about!" exclaimed Captain Obvious. "Hang on a minute," said Steve. He pulled up the source files for the new features deployed earlier that day. A glance at the beacon feature branch code made it clear what went wrong:
>幾秒鐘之后,頁面重新刷新,然后一個重要的紅色提醒出現在頂部。“這就是我提到的信標!”裝傻隊長說道。“稍等一下啊,”斯蒂夫說到。他從前些天部署的新功能拉取了原文件。稍微看下信標功能的分支代碼能夠搞清楚到底哪里出問題了:
>
```python
if switch_is_active(request, 'beacon') and not
request.user.is_staff():
# Display the beacon
```
>“各位不好意思,”斯蒂夫說道。“這里出現了一個邏輯錯誤。我們一時疏忽就將該功能的啟用開放給了所有人,而不是僅有站點注冊用戶來開啟。現在我把這個功能給關閉了。對于因此而引起的混亂我表示歉意。”
>"So, there was no emergency?" said Captain with a disappointed look. Hexa put an arm on his shoulder and said "I am afraid not, Captain." Suddenly, there was a loud crash and everyone ran to the hallway. A man had apparently landed in the office through one of the floor-to-ceiling glass walls. Shaking off shards of broken glass, he stood up. "Sorry, I came as fast as I could," he said, "Am I late to the party?" Hexa laughed. "No, Blitz. Been waiting for you to join," she said.
>“這么說,根本沒什么要緊事嘍?”隊長帶著一臉失望?的說道。Hexa一面把手搭在他肩膀上一邊說道:“我也不希望發生什么事情,隊長。”突然之間,室內閃現出一團云,屋里的人都趕緊跑開了。
## 增強用于admin的模型
admin應用足夠聰明,因此它可以自動地從模型發現非常多東西。可是,有時候推定信息需要改進。這通常涉及到模型自身添加一個屬性或者一個方法(而不是在`ModelAdmin`類中添加)。
首先,為了更好的說明問題,讓我們來看一個包括admin接口而增強模型的例子:
```python
# models.py
class SuperHero(models.Model):
name = models.CharField(max_length=100)
added_on = models.DateTimeField(auto_now_add=True)
def __str__(self):
return "{0} - {1:%Y-%m-%d %H:%M:%S}".format(self.name,
self.added_on)
def get_absolute_url(self):
return reverse('superhero.views.details', args=[self.id])
class Meta:
ordering = ["-added_on"]
verbose_name = "superhero"
verbose_name_plural = "superheroes"
```
我們看看admin是如何利用這些非字段屬性的:
```
? __str__(): 沒有它的話,superhero條目的列表看上去會極其無趣的。每一個條目都請清楚地顯示為`<SuperHero: SuperHero object>`。試著去包含對象自己的`str`表現形式的唯一信息(在Python2中的代碼,是`unicode`表現形式),比如對象自己的名稱或者版本。任何能夠有助于admin不含糊地理解的東西都是大有裨益的。
? get_absolute_url(): 如果你喜歡在網站中的admin視圖和對象詳細視圖之間切換,該屬性會很方便的。如果該方法被定義,那么在admin頁面中對象的編輯頁面的右上會出現一個叫做`”View on site“`的按鈕。
? ordering: 如果沒有這個元選項,你的條目會在數據數據庫返回后以任意順序出現。你也可以想象一下,如果你有大量的項目對于管理來說可不是好玩的。刷新
? verbose_name: 如果你忽略該屬性,模型的名稱會從`CamelCase`轉換到`camelcase`。這個例子中,“super hero`看上去不那么優美,因此最好是明確你要如何將用戶可讀的名稱顯示在admin接口。
? verbose_name_plural: 再者,忽略該選項能夠給你帶來比較有趣的結果。因為Django簡單地將一個‘s’預加到單詞,復數形式的superhero顯示為`”superheros“`(仍舊出現在admin前面的頁面)。因此,這里最好是正確地定義它。
```
這里建議你定義前面的`Meta`屬性和方法,而不僅僅是只用于admin接口,而且也是為了在shell中和日志文件中,等等中更好的表現內容。
當然,你也可以像下面這樣,通過創建一個`ModelAdmin`類來進一步改進在admin里的顯示:
```python
# admin.py
class SuperHeroAdmin(admin.ModelAdmin):
list_display = ('name', 'added_on')
search_fields = ["name"]
ordering = ["name"]
admin.site.register(models.SuperHero, SuperHeroAdmin)
```
我們來看看這些更為嚴密的選項:
- list-display: 該選項在一個表格形式的表單中該顯示模型實例。它顯示每個獨立可排序列的字段。如果你希望看到模型的多個屬性,該選項是非常理想的。
- search_fields: 該選項在列表上面顯示一個搜索框。任何的輸入的搜索項都可以搜索到對應的引用字段。因此,僅有CharField或者TextField這樣的文本字段被引用。
- ordering: 該選項優先于模型的默認順序。在admin后臺管理中選擇一個不同的順序時,會很有用的。
圖片:略
前面的截圖插入內容為:
- 插入內容1: 不使用str或者Meta屬性
- 插入內容2: 使用增強的模型meta屬性
- 插入內容3: 使用定制的ModelAdmin
這里我們僅僅提到了一個常用的amdin選項子集。某些類型的網站會重度地使用admin接口。在這樣地情況下,這里強烈建議你徹徹底底搞明白Django文檔的admin部分。
## 不應該讓所有人都成為admin
Since admin interfaces are so easy to create, people tend to misuse them. Some give early users admin access by merely turning on their 'staff' flag. Soon such users begin making feature requests, mistaking the admin interface to be the actual application interface.
因為admin接口很輕松就可以創建了,所以有可能被濫用。
Unfortunately, this is not what the admin interface is for. As the flag suggests, it is an internal tool for the staff to enter content. It is production-ready but not really intended for the end users of your website.
It is best to use admin for simple data entry. For example, in a project I had reviewed, every teacher was made an admin for a Django application managing university courses. This was a poor decision since the admin interface confused the teachers.
不幸的是這不是admin接口的本來目的。
The workflow for scheduling a class involves checking the schedules of other teachers and students. Using the admin interface gives them a direct view of the database. There is very little control over how the data gets modified by the admin.
So, keep the set of people with admin access as small as possible. Make changes via admin sparingly, unless it is simple data entry such as adding an article's content.
因此,你要保持能夠訪問admin人群數量盡可能少。通過admin操作地變更要謹慎,除非是添加一篇文章內容這樣地簡單數據條目操作。
>###提示
**最佳實踐**
不要讓admin方法終端用戶。
Ensure that all your admins understand the data inconsistencies that can arise from making changes through the admin. If possible, record manually or use apps, such as django-audit-loglog that can keep a log of admin changes made for future reference.
確保
In the case of the university example, we created a separate interface for teachers, such as a course builder. These tools will be visible and accessible only if the user has a teacher profile.
Essentially, rectifying most misuses of the admin interface involves creating more powerful tools for certain sets of users. However, don't take the easy (and wrong) path of granting them admin access.
## admin接口的定制
開箱即用單admin接口對于準備使用它的人來說非常有用。不幸的是,很多人都假設改變Django的admin肯定非常困難,然后就撒手不管了。實際上,admin是屬于極其易于定制的,它的外觀可以用最小的努力就得以改變。
## 改變標題
Many users of the admin interface might be stumped by the heading—Django administration. It might be more helpful to change this to something customized such as MySite admin or something cool such as SuperBook Secret Area.
很多admin用戶或許被標題——Django administration給難住了。
要改變標題是很容易的。在站點的urls.py中添加下面這行內容就好了:
```python
admin.site.site_header = "SuperBook Secret Area"
```
## 改變基本樣式
幾乎所有的admin頁面都擴展自叫做admin/base_site.html都公共基本模板。這意味著只需用到少量都HTML和CSS的知識,因此你可以定制所有的排序以改變admin接口的外觀和視覺。
先簡單地在任意地模板目錄中創建一個名稱為admin的目錄。然后,從Django源目錄復制文件base_site.html,并按照自己的需要做相應的變更。如果你不知道模板的位置,那么在Django shell中運行下面的命令就是了:
```python
>>> from os.path import join
>>> from django.contrib import admin
>>> print(join(admin.__path__[0], "templates", "admin"))
```
例如,定制admin的基礎模板,你可以改變admin接口的整個字體為谷歌字體“Special Elite”,谷歌的這個字體看上去非常的厚重。你需要使用以下內容在項目中的模板目錄中添加一個文件admin/base_site.html;
```python
{% extends "admin/base.html" %}
{% block extrastyle %}
<link href='http://fonts.googleapis.com/css?family=Special+Elite'
rel='stylesheet' type='text/css'>
<style type="text/css">
body, td, th, input {
font-family: 'Special Elite', cursive;
} </style>
{% endblock %}
```
該代碼通過添加一個附加的樣式表來重寫與字體相關的樣式,而且附加的樣式會應用于每個admin的頁面。
## 添加富文本編輯器
有時候,你需要在admin接口中使用JavaScript代碼。常見的一個需求就是對TextField使用CKEditor這樣的HTML編輯器。
在Django中有多種實現這個編輯器的方法,例如,對ModleAdmin類使用一個Media內部類。不過,我發現擴展admin的change_form模板是最方便的方法。
例如,假如你擁有一個稱作Posts的應用,那么你需要去在template/admin/posts/directory目錄之內新建一個稱作change_form.html的文件。如果你需要在這個應用內的任意模型中顯示CKEditor,那么這個文件的內容是這個樣子的:
```python
/home/arun/env/sbenv/lib/python3.4/site-packages/django/contrib/admin/templates/admin
```
該文件中的最后一行是所有admin模板中的位置所在。你可以重寫或者擴展這些模板中的任何一個。可以參考下一小節的擴展模板的例子。
```python
{% extends "admin/change_form.html" %}
{% block footer %}
{{ block.super }}
<script src="//cdn.ckeditor.com/4.4.4/standard/ckeditor.js"></
script>
<script> CKEDITOR.replace("id_message", {
toolbar: [
[ 'Bold', 'Italic', '-', 'NumberedList', 'BulletedList'],],
width: 600,
});
</script>
<style type="text/css">
.cke { clear: both; }
</style>
{% endblock %}
```
高亮的部分是用于我們希望將一個普通的文本輸入框加強為富文本編輯器的表單元素的自動創建的ID。這些腳本和樣式眼睛被添加到了footer塊,這樣表單元素可以在自身被改變之前,于DOM中創建。
## 使用Bootstrap主題的admin
總的來說,admin接口已經設計非常好了。不過,由于它是在2006年設計的,而且是為了顯示效果的通用性做出的設計。因此,它沒有適應mobile設備,或者是擁有其他的已經成為今日事實標準的細節部分。
毫不奇怪的是admin定制中最常見要求是確定是否可以繼承Bootstrap。有多個包可以實現這個需求,比如django-admin-bootstrapped或者djangosuit。
這些包提供了開箱即用的基于Bootstrap主題的模板,而不是你自己去重新編寫所有的admin模板。因為基于Bootstrap,所以它們擁有響應式功能,而且包含了多種部件和組件。
## 徹底檢查
admin接口也已經在我們的嘗試下完全的重寫了。Grappelli是一個非常流行皮膚,它能夠利用功能擴展Django admin,比如自動查詢和折疊嵌套。使用django-admin-tools,你可以獲得一個可定制的面板和工具欄。
There have been attempts made to completely rewrite the admin, such as django-admin2 and nexus, which did not gain any significant adoption. There is even an official proposal called AdminNext to revamp the entire admin app. Considering the size, complexity, and popularity of the existing admin, any such effort is expected to take a significant amount of time.
這里也有完全重寫admin的嘗試,比如django-admin2和nexus,它們不會著重使用的。
## 保護admin
The admin interface of your site gives access to almost every piece of data stored. So, don't leave the metaphorical gate lightly guarded. In fact, one of the only telltale signs that someone runs Django is that, when you navigate to http://example. com/admin/, you will be greeted by the blue login screen.
網站的admin接口幾乎訪問了每一塊存儲的數據。因此,不要留下缺少保護的后門。實際上,
在生產環境中,我們建議你將這個地址改為不太顯眼的地址。在項目的根urls.py中盡可能簡單地變更該行:
```python
url(r'^secretarea/', include(admin.site.urls)),
```
一個稍微更加成熟的做法是在默認位置使用假的admin站點或者蜜罐(參見第三方包`django-admin-honeypot`)。不過,最好的選擇對admin站點范圍內使用HTTPS,因為常規的HTTP會把所有的數據以明文格式發送到網絡中去。
檢查web服務器的文檔,看看如何為到admin的請求設置HTTPS。在Nginx上面,設置這個連接方式非常的簡單,涉及到的有指定SSL認證位置。最后,將所有到admin頁面的HTTP請求重定向到HTTPS,現在可以安生地睡個好覺了。
The following pattern is not strictly limited to the admin interface but it is nonetheless included in this chapter, as it is often controlled in the admin.
## 模式- 功能標識
遇到的問題:對用戶發布的新功能,以及在生產環境中部署的對應代碼都應當是互相獨立的。
解決方法:在部署之后,使用功能標識有選擇性地啟動或者禁用功能。
### 問題細節
Rolling out frequent bug fixes and new features to production is common today. Many of these changes are unnoticed by users. However, new features that have significant impact in terms of usability or performance ought to be rolled out in a phased manner. In other words, deployment should be decoupled from a release.
現如今,慣常的bug修復和新功能在生產環境中是很常見的。對于用戶這些改變中很多的改變是不被通知的。不過,
Simplistic release processes activate new features as soon as they are deployed. This can potentially have catastrophic results ranging from user issues (swamping your support resources) to performance issues (causing downtime).
過于簡單發布過程
Hence, in large sites it is important to decouple deployment of new features in production and activate them. Even if they are activated, they are sometimes seen only by a select group of users. This select group can be staff or a sample set of customers for trial purposes.
因此,在生產環境中對于大型站點來說最重要的是解構新功能的部署,并激活這些新功能。即使這些新功能被激活了,它們也只是僅僅對被選擇了的用戶可見。這個被挑選出來的組可以是站點注冊成員,也可以是一組簡單的出于試驗目的而存在的用戶。
### 解決方法細節
Many sites control the activation of new features using Feature Flags. A feature flag is a switch in your code that determines whether a feature should be made available to certain customers.
很多網站的新功能激活是透過功能標識實現的。功能標識是一個代碼中的可以決定一個功能是否對某些用戶開放的開關。
Django有多個提供功能標識的包,比如 gargoyle 和 django-waffle。這些包在網站的數據庫中存儲功能標識。他們能夠透過admin接口或者管理命令進行激活或者失效。因此,每一種環境(生產、測試、開發、等等)都可以擁有屬于自己的一組激活功能。
Feature flags were originally documented, as used in Flickr (See http://code. flickr.net/2009/12/02/flipping-out/). They managed a code repository without any branches, that is, everything was checked into the mainline. They
also deployed this code into production several times a day. If they found out
that a new feature broke anything in production or increased load on the database, then they simply disabled it by turning that feature flag off.
功能旗幟是得到原生的文檔支持,一如用在Flickr。它們不用任何分支管理代碼倉庫,即,一切內容都記錄到主線中。它們在一天可以多次部署到生產環境中。如果在生產環境中發現新的功能破壞了任何其他東西,或者增加了數據庫的負載,那么它們都通過關閉功能旗幟來簡單的禁用。
Feature flags can be used for various other situations (the following examples use django-waffle):
功能標識可以用于多種情況(下面的例子使用的是django-waffle):
- Trials:
A feature flag can also be conditionally active for certain users.
These can be your own staff or certain early adopters than you may be targeting as follows:
- 試用:
功能標識也可以根據條件針對部分用戶激活。
如下,這些用戶可以是站點的注冊成員,或者某些你想指定監護人:
```python
def my_view(request):
if flag_is_active(request, 'flag_name'):
# Behavior if flag is active.
```
Sites can run several such trials in parallel, so different sets of users might actually have different user experiences. Metrics and feedback are collected from such controlled tests before wider deployment.
站點可以平行的運行多個試用,這樣不同組的用戶實際上可以擁有不同的用戶體驗。在大范圍部署之前,可以從這里可控制的測試中收集質量和反饋。
- A/B testing: This is quite similar to trials except that users are selected randomly within a controlled experiment. This is quite common in web design to identify which changes can increase the conversion rates. This is how such a view can be written:
- A/B測試:該測試很類似于體驗測試,除了用戶在被控制的試驗中隨機地選擇用戶。對于web設計來說識別出哪個變更能夠增加轉換速率是相當常見的。這也展示這樣的一個視圖是如何編寫的:
```python
def my_view(request):
if sample_is_active(request, 'design_name'):
# Behavior for test sample. 針對測試例子的具體行為
```
- Performance testing: Sometimes, it is hard to measure the impact of a feature on server performance. In such cases, it is best to activate the flag only for a small percentage of users first. The percentage of activations can be gradually increased if the performance is within the expected limits.
- 性能測試:有時候,很難去測量服務器上一個功能性能影響。這類例子中,最好是首先僅對一小部分激活旗幟。如果性能存在未預料地的限制,激活百分比可以逐漸地增加。
- Limit externalities: We can also use feature flags as a site-wide feature switch that reflects the availability of its services. For example, downtime in external services such as Amazon S3 can result in users facing error messages while they perform actions, such as uploading photos.
- 擴展性的限制:我們也可以使用功能旗幟
When the external service is down for extended periods, a feature flag can be deactivated that would disable the upload button and/or show a more helpful message about the downtime. This simple feature saves the user's time and provides a better user experience:
當擴展服務因為擴展周期而關閉時,新的功能旗幟被取消激活將會禁用上傳按鈕同時/或者顯示關于關閉時間更為有幫助的消息。這個簡單的功能保存了用戶的時間并提供了更好的用戶體驗:
```python
def my_view(request):
if switch_is_active('s3_down'):
# Disable uploads and show it is downtime 禁用上傳并在被禁用時顯示
```
The main disadvantage of this approach is that the code gets littered with conditional checks. However, this can be controlled by periodic code cleanups that remove checks for fully accepted features and prune out permanently deactivated features.
這個方法的主要缺點是按照某些條件檢查代碼會變得垃圾。不過,
## 總結
本章我們探究了Django的內建應用admin。我們發現了它不僅僅是非常好用的開箱即用,而且可以實現各種定制,以改進它的外觀和功能。
下一章,我們將會通過思考多種模式和常見用法來學習在Django中如何更有效的使用表單。