這篇教程從第6節(jié)結(jié)束的地方開始,我們繼續(xù)開發(fā)Web投票應(yīng)用偷线,我們將注意力放在定制Django的自動生成式管理站點,我們在第2節(jié)的時候用過一次。
定制admin表單
通過使用 admin.site.register(Question)
來注冊Question
优训,Django可以組織一個默認的表格展示形式。很常見的情況是各聘,你希望定制admin表單看起來的樣子以及它的工作方式揣非。你可以通過在注冊對象的時候通過告知Django相關(guān)的選項來完成這樣的定制。
讓我們來看一下是怎么做的躲因,通過重新調(diào)整編輯表單上的字段順序早敬。使用下面的代碼替換admin.site.register(Question)
:
from django.contrib import admin
from .models import Question
class QuestionAdmin(admin.ModelAdmin):
fields = ['pub_date', 'question_text']
admin.site.register(Question, QuestionAdmin)
你可以遵循這種模式—— 創(chuàng)建一個模型管理類,然后把它作為admin.site.register()
的第二個參數(shù)大脉。任何時候你需要修改模型的管理選項搞监。
上面特定的修改會讓“Publication date”字段在“Question”字段的前面,像下面這樣:
只有兩個字段的話不是特別明顯镰矿,但是對于由很多字段的管理表單的話琐驴,選擇一個直觀的順序是一個非常重要且有用的細節(jié)。
說到有很多字段的表單秤标,你可能會想將表單切割成字段集合:
from django.contrib import admin
from .models import Question
class QuestionAdmin(admin.ModelAdmin):
fieldsets = [
(None, {'fields': ['question_text']}),
('Date information', {'fields': ['pub_date']}),
]
admin.site.register(Question, QuestionAdmin)
在字段集合里的每一個元組的第一個元素是字段集合的標題绝淡,下面是我們的表單看起來的樣子。
添加關(guān)聯(lián)對象
好了苍姜,我們現(xiàn)在以及有了我們的Question管理頁面牢酵,但是一個Question有多個Choice,并且管理頁面并沒有顯示Choice衙猪。
有兩種方式來解決這種問題馍乙,第一種是使用admin來注冊Choice布近,就像我們剛才注冊Question一樣,這非常簡單:
from django.contrib import admin
from .models import Choice, Question
# ...
admin.site.register(Choice)
然后再Django管理頁面就能看到Choice選項了丝格,"Add choice"表單看起來像下面這樣:
在這個表單里撑瞧,"Question"字段是一個選擇框,里面包含了數(shù)據(jù)庫里的所有Question显蝌。Django知道在管理頁面將一個ForeignKey當(dāng)作一個選擇框季蚂。在我們的例子里,只有一個question琅束。
也要注意扭屁,在“Question”旁邊的“Add another”按鈕,每個帶有外鍵管理到其他對象的對象都會自動生成這個按鈕涩禀。當(dāng)你點擊“Add another”的時候料滥,你會得到一個彈出式窗口,關(guān)聯(lián)到“Add Question”表單艾船。如果你在這個表單里添加一個問題葵腹,然后點擊“Save”,Django會自動把這個question保存到數(shù)據(jù)庫屿岂,并且動態(tài)的將它也添加到你剛才看到的“Add Choice”表單的選擇框里践宴。
但是,實際上爷怀,這樣添加Choice對象到系統(tǒng)里阻肩,確實是一種低效的方式。在你添加Question對象的時候直接添加很多Choice运授,這樣的方式會更好烤惊,讓我們來試試。
移除Choice模型的register()調(diào)用吁朦,然后編輯Question注冊代碼柒室,像下面這:
from django.contrib import admin
from .models import Choice, Question
class ChoiceInline(admin.StackedInline):
model = Choice
extra = 3
class QuestionAdmin(admin.ModelAdmin):
fieldsets = [
(None, {'fields': ['question_text']}),
('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}),
]
inlines = [ChoiceInline]
admin.site.register(Question, QuestionAdmin)
這告訴Django:“要在Question管理頁面編輯Choice對象。默認情況下逗宜,為3個choice提供足夠的字段雄右。”
加載"Add question"頁面纺讲,看一下它的樣式擂仍。
它看起來像這樣:關(guān)聯(lián)的Choices有3個槽——通過Choice的extra設(shè)置的數(shù)量。每次你返回到創(chuàng)建好的Question的“Change”頁面刻诊,你都會看到3個額外的槽防楷。
在當(dāng)前三個槽的結(jié)尾處牺丙,你會看到一個“Add another Choice”鏈接则涯,如果你點擊它复局,會添加另外一個新的槽。如果你想移除已經(jīng)添加的槽粟判,你可以點擊已經(jīng)添加的槽的右上方的X來刪除它亿昏。下面的圖片顯示一個已經(jīng)添加的槽:
但是還有一個小問題就是,它需要很多屏幕空間來顯示已經(jīng)創(chuàng)建好的档礁,和Question相關(guān)聯(lián)的Choice對象的所有字段角钩。基于這個原因呻澜,Django提供一個表格形式來顯示內(nèi)聯(lián)的對象递礼。你只需要修改ChoiceInline的聲明類型。:
class ChoiceInline(admin.TabularInline):
#...
使用TabularInline(替換StackedInline)羹幸,關(guān)聯(lián)的對象會以更緊湊脊髓,基于表格的格式來顯示:
注意,有一個額外的"Delete"列栅受,用來刪除通過"Add Another Choice"按鈕添加的行将硝,或者已經(jīng)保存的行。
定制管理頁面的修改列表
現(xiàn)在Question的管理頁面看起來很棒屏镊,現(xiàn)在我們來對“change list”頁面做一些調(diào)整依疼。這個頁面是用來顯示系統(tǒng)里的所有questions的頁面。
下面是它現(xiàn)在看起來的樣子:
默認情況下而芥,Django顯示每個對象的str()律罢,但是有些時候我們可以顯示不同的字段的話可能更有幫助。要實現(xiàn)這樣的功能棍丐,我們使用list_display這個管理選項弟翘,這是一個用來在對象的“change list”頁面要顯示的字段名稱組成的元組。
class QuestionAdmin(admin.ModelAdmin):
# ...
list_display = ('question_text', 'pub_date')
為了更方便骄酗,我們把第2節(jié)里的was_published_recently()方法也加上:
class QuestionAdmin(admin.ModelAdmin):
# ...
list_display = ('question_text', 'pub_date', 'was_published_recently')
現(xiàn)在question的修改頁面看起來像下面的樣子:
你可以點擊列的頭部來通過值排序——除了was_published_recently頭部, 因為通過一個不確定的方法的輸出值來排序,目前還是不支持的.也要注意,was_published_recently這一列的頭部默認情況下就是方法的名稱(使用下劃線替換空格),并且每一行包含方法輸出的字符串格式.
你可以通過這個方法(在polls/models里)的一些屬性來提高頁面的可讀性,像下面這樣:
class Question(models.Model):
# ...
def was_published_recently(self):
now = timezone.now()
return now - datetime.timedelta(days=1) <= self.pub_date <= now
was_published_recently.admin_order_field = 'pub_date'
was_published_recently.boolean = True
was_published_recently.short_description = 'Published recently?'
關(guān)于這個方法特性的更多信息,可以看list_display.
再次編輯你的polls/admin.py文件,然后添加一個修改后的方法到Question的修改列表頁面:通過使用list_filter來過濾Question. 把下面的行添加到QuestionAdmin里:
list_filter = ['pub_date']
這行代碼提供了一個"Filter"側(cè)邊欄,讓用戶可以通過pub_date字段過濾修改列表,如下所示:
過濾器的類型依賴于你要過濾的字段的類型,因為pub_date是一個DateTimeField,Django知道給出一個相近的選項:"Any date", "Today", "Past 7 days", "This Month", "This year".
這種篩選方式非常好,我們再添加一些搜索的兼容性:
search_fields = ['question_text']
這行代碼會再修改列表頂部添加一個搜索框.當(dāng)有人輸入搜索選項的時候,Django會搜索question_text字段稀余。你可以使用任意多個你想要的字段,但是因為在后臺使用的是LIKE查詢,所以限制了搜索字段的數(shù)量到合理的值趋翻,讓數(shù)據(jù)庫搜索的時候更簡單睛琳。
修改列表也給了你一個免費的分頁功能,默認的是顯示一頁100個選項踏烙。Change_list_pagination师骗,search_boxes,filters讨惩,date-hierarchies和column-headerr-ordering會像你認為的那樣工作(個人理解是根據(jù)他們函數(shù)的名稱那樣工作辟癌,比如filter就是用來過濾)。
定制管理頁面的外觀和感覺
顯然荐捻,在每個管理頁面的頂端都有"Django administration"很扯淡黍少,它們僅僅只是占位符內(nèi)容寡夹。
但是直接使用Django的模板系統(tǒng),這些也很容易修改厂置。Django的管理頁面是它自己支持的菩掏,并且它的接口使用Django自己的模板系統(tǒng)。
定制你自己的項目的模板
直接在你的項目目錄(包含manage.py的目錄)里創(chuàng)建一個templates目錄昵济,模板可以在你的文件系統(tǒng)任何位置智绸,只要Django可以訪問到就行。(Django可以用你服務(wù)器上的任何用戶來運行)访忿,但是瞧栗,保證模板在你的項目里,是一個指的遵守的良好規(guī)則海铆。
打開你的設(shè)置文件(mysite/settings.py沼溜,記住)然后添加一個DIRS選項到TEMPLATES設(shè)置里:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
DIRS是一個在加載Django模板時候需要檢查的文件系統(tǒng)目錄列表游添,它是一個搜索路徑系草。
組織模板
和靜態(tài)文件相似,我們可以吧所有的模板都放到一起唆涝,在一個大的模板目錄里找都。并且它也可以很好地工作。但是廊酣,屬于特定應(yīng)用的模板應(yīng)該位于這個應(yīng)用自己的模板目錄里(例如polls/templates)而不是在項目的模板目錄里(templates)能耻。我們會在重復(fù)使用app教程里討論我們?yōu)槭裁匆@樣做。
現(xiàn)在在templates里面創(chuàng)建一個叫做admin的目錄亡驰,然后在Django它自己的(django/contrib/admin/templates)的源代碼的Django默認管理模板目錄里復(fù)制admin/base_site.html模板到admin目錄下面晓猛。
Django源文件在哪?
如果在你的系統(tǒng)上查找Django源文件比較困難凡辱,運行下面的命令:
$ python -c "import django;
print(django.__path__)"
然后編輯base_site.html文件戒职,將{{ site_header|default:_('Django administration') }} (包括正確的括號)替換為你自己的站點名稱。最后你的代碼必須像下面這樣:
{% block branding %}
<h1 id="site-name"><a href="{% url 'admin:index' %}">Polls Administration</a></h1>
{% endblock %}
我們用這種方式告訴你怎么覆蓋模板透乾,在一個實際的項目里洪燥,你可能會使用django.contrib.admin.AdminSite.site_header屬性,來更簡單地完成特定的定制乳乌。
這個模板文件包含大量像{% block branding %}和{{ title }}這樣的文本捧韵。{% 和 {{ 標簽是Django模板語言的一部分。當(dāng)django渲染admin/base_site.html的時候汉操,這個模板語言會被鑒定并生成最終的HTML頁面再来,就像我們在第三節(jié)里看的那樣。
要注意磷瘤,任何Django的默認管理模板都能被覆蓋芒篷,要覆蓋一個模板搜变,只需要像你在base_site.html里做的那樣。把它從默認目錄里復(fù)制出來梭伐,放到你的定制目錄里去,然后做修改仰担。
定制你的應(yīng)用的模板
精明的讀者可能會問:如果默認情況下DIRS是空的糊识,Django怎么找到默認的管理頁面?回答是摔蓝,只要APP_DIRS設(shè)置為True赂苗,Django會自動在每一個應(yīng)用包里找templates子目錄,作為一個返回值使用(不要忘了django.contrib.admin是一個應(yīng)用)贮尉。
我們的投票應(yīng)用不是非常復(fù)雜拌滋,并且不需要定制admin 模板,但是如果它成長得更加復(fù)雜猜谚,為了實現(xiàn)它的一些功能败砂,需要修改Django的標準管理模板。直接修改應(yīng)用的模板比修改項目的模板更明智魏铅。通過這種方式昌犹,你可以再任何心的項目里包含投票應(yīng)用,并且保證它能夠找到自己定制后的模板览芳。
Django怎么查找它的模板的更多細節(jié)斜姥,請查看模板加載文檔。
定制管理首頁
在一些類似的說明中沧竟,你可能會想定制Django管理首頁的外觀和感覺铸敏。
默認情況下,它用字母排序來顯示所有在INSTALLED_APPS里悟泵,且使用admin應(yīng)用注冊了的應(yīng)用杈笔。你可能想對布局進行大的修改,畢竟糕非,首頁可能是管理頁面里最重要的頁面桩撮,它應(yīng)該易于使用。
需要定制的模板是admin/index.html(和前面部分里對admin/base_site.html做的一樣— 把它從默認的目錄里復(fù)制到你的定制模板目錄里)峰弹。編輯這個文件店量,然后你會看到它使用一個叫做app_list的模板變量,這個變量包含每個安裝了的Django app鞠呈。為了替換這個融师,你可以使用硬編碼的鏈接指到特定對象的管理頁面,用你自己覺得最好的方式來做蚁吝。
下一步做什么旱爆?
教程的初始部分到這就結(jié)束了舀射,與此同時,你可能想在where to go from here里看到一些提示怀伦。
如果你對Python打包很熟悉脆烟,且對學(xué)習(xí)怎么把投票應(yīng)用轉(zhuǎn)變?yōu)橐粋€可重復(fù)使用的app非常感興趣,可以看高級教程:怎么寫重復(fù)使用的app房待。