本教程上接Tutorial 6 稀蟋。 我們繼續(xù)網(wǎng)頁投票應(yīng)用,并將重點(diǎn)定制Django自動生成的管理后臺站點(diǎn)骏融,這是我們在Tutorial 2中首先探討的。
自定義管理后臺的表單
只需使用admin.site.register(Question)
注冊Question
模型档玻,Django就能構(gòu)造一個默認(rèn)的表單表示茫藏。 通常境钟,你會想要自定義管理界面中表單的外觀和功能匣缘。 你可以通過在注冊對象的時(shí)候告知Django一些你想要的選項(xiàng)來完成。
讓我們看看如何通過重新排序編輯表單上的字段來實(shí)現(xiàn)。 將admin.site.register(Question)
行替換成:
polls/admin.py
from django.contrib import admin
from .models import Question
class QuestionAdmin(admin.ModelAdmin):
fields = ['pub_date', 'question_text']
admin.site.register(Question, QuestionAdmin)
任何時(shí)候你需要更改模型的管理選項(xiàng),你將遵循此模式 — 創(chuàng)建一個模型管理類,然后將其作為第二個參數(shù)傳遞給admin.site.register()泊窘。
上面那特定的更改,使得“Publication date”字段排在“Question”字段前面:
僅有兩個字段不會令你印象深刻瓜贾,但是當(dāng)管理有許多字段的表單時(shí),選擇一個直觀的排序方式是一個重要而實(shí)用的細(xì)節(jié)祭芦。
說到有許多字段的表單憔鬼,你可能想把表單分割成字段集:
polls/admin.py
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)
fieldsets
中每個元組的第一個元素是字段集的標(biāo)題龟劲。 以下是我們的對象表單現(xiàn)在的樣子:
添加關(guān)聯(lián)的對象
好的轴或,我們有我們的Question管理頁面,但Question
有多個Choice
蚕愤,而管理頁面沒有顯示Choice。
然鵝
有兩種方法來解決這個問題萍诱。 第一種是像我們?yōu)镼uestion做的一樣污呼,在管理站點(diǎn)中注冊Choice裕坊。 這很簡單:
polls/admin.py
from django.contrib import admin
from .models import Choice, Question
# ...
admin.site.register(Choice)
現(xiàn)在燕酷,可以在Django管理站點(diǎn)中管理“Choices”。 “Add choice”表單看起來像這樣:
在這個表單中苗缩,“Question”字段是一個可選的選項(xiàng)框,包含數(shù)據(jù)庫中所有的Question苹享。 Django中一個ForeignKey
應(yīng)該在管理界面中顯示為一個<select>
選框。 在我們的例子中得问,目前選框里只有一個Question。
另請注意“Question”旁邊的“Add Another”鏈接宫纬。具有ForeignKey
關(guān)系的每個對象都默認(rèn)有一個這樣的鏈接。 當(dāng)你點(diǎn)擊“Add Another”時(shí)漓骚,你將看到一個帶有“Add question”表單的彈出窗口。 如果你在該窗口中添加了一個Question并單擊“Save”蝌蹂,Django會將該Question保存到數(shù)據(jù)庫中,并將其作為選定的選項(xiàng)動態(tài)添加到你正在查看的“Add choice”表單中剃允。
但事實(shí)上,這不是一種高效的方式來添加Choice
對象到系統(tǒng)中斥废。 在創(chuàng)建Question
對象的同時(shí)可以直接添加一組Choice將會更好给郊。 讓我們實(shí)現(xiàn)這個功能牡肉。
移除對Choice
模型的register()
調(diào)用淆九。 然后將Question
的注冊代碼編輯為:
polls/admin.py
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:“Choice對象在Question的管理界面中編輯。 默認(rèn)提供3個Choice跪另∨《叮”
打開“Add question”頁面:
它這樣工作:有三個所關(guān)聯(lián)的Choice —— 由extra指定 —— 每次你回到已經(jīng)存在對象的"Change"頁面時(shí),都會額外地獲得三個空白Choice擦盾。
在現(xiàn)有的三個Choice的底部淌哟,你會發(fā)現(xiàn)一個“Add another Choice”的鏈接迹卢。 如果你點(diǎn)擊它徒仓,就會增加一個新的空白Choice。 如果你想移除一個新增加的空白Choice,可以點(diǎn)擊其右上角的X症见。 請注意,你無法移除那最初的三個空白Choice芋肠。 下面的圖片展示新增加的一個空白Choice:
還有個小問題遵蚜。 顯示所有關(guān)聯(lián)的Choice 對象的字段占用大量的屏幕空間帖池。 為此吭净,Django提供了一種顯示內(nèi)聯(lián)相關(guān)對象的表格方式;你只需將ChoiceInline聲明更改為:
polls/admin.py
class ChoiceInline(admin.TabularInline):
#...
使用TabularInline
(而不是StackedInline
)寂殉,這些相關(guān)聯(lián)的對象顯示成緊湊的、基于表格的形式:
你有沒有注意有一個額外的列“Delete?”文兢,用“Add Another Choice”按鈕添加的行和已經(jīng)保存的行可以通過它來刪除。
自定義admin change列表
現(xiàn)在姆坚,Question管理界面看起來已經(jīng)很好了实愚,讓我們再來稍微調(diào)整一下“變更列表”界面 —— 該界面顯示系統(tǒng)中所有的Question兼呵。
下面是目前為止它的樣子:
默認(rèn)地腊敲,Django顯示每個對象的str()
返回的內(nèi)容。 但有時(shí)如果我們能顯示個別的字段將很有幫助碰辅。 我們使用list_display
選項(xiàng)來實(shí)現(xiàn)這個功能,它是一個要顯示的字段名稱的元組凌彬,在對象的變更列表頁面上作為列顯示:
class QuestionAdmin(admin.ModelAdmin):
# ...
list_display = ('question_text', 'pub_date')
為了方便查看,我們還添加了Tutorial 2中的was_published_recently()
方法:
class QuestionAdmin(admin.ModelAdmin):
# ...
list_display = ('question_text', 'pub_date', 'was_published_recently')
現(xiàn)在铲敛,Question變更列表頁面看起來就像如下所示:
你可以點(diǎn)擊其中一列的頭部來讓列表按照這列的值來進(jìn)行排序 —— 除了was_published_recently這列的頭部会钝,因?yàn)镈jango不支持按照隨便一個方法的輸出進(jìn)行排序。 另請注意,默認(rèn)情況下先鱼,was_published_recently的列標(biāo)題是方法的名稱(下劃線替換為空格),每行包含輸出的字符串表示形式段审。
通過給這個方法(在polls/models.py
)提供一些屬性改進(jìn),如下所示:
polls/models.py
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
文件來改進(jìn)Question
變更列表頁面:使用 list_filter
來添加過濾器寺枉。 將下面這行添加進(jìn)QuestionAdmin
:
polls/admin.py
class Question(models.Model):
...
list_filter = ['pub_date']
這行代碼添加一個“Filter”側(cè)邊欄绷落,可以使人們通過pub_date
字段對變更列表進(jìn)行過濾:
顯示的過濾器類型取決于你所使用的字段類型。 因?yàn)?code>pub_date是一個 DateTimeField
砌烁,Django知道提供適當(dāng)?shù)倪^濾器選項(xiàng):“任何日期”,“今天”函喉,“過去7天”,“這個月”管呵,“這年”。
這樣看起來有些像我們想要的樣子了账锹。 讓我們再來添加一些搜索功能:
search_fields = ['question_text']
這行代碼在變更列表的頂部添加了一個搜索框。 當(dāng)有人將搜索的內(nèi)容輸入搜索框坷襟,Django將在question_text
字段中進(jìn)行搜索。 你可以使用任意數(shù)量的字段進(jìn)行搜索 —— 但由于它在后臺使用LIKE
進(jìn)行查詢廓奕,所以限制搜索字段的數(shù)量會使數(shù)據(jù)庫查詢更加容易。
現(xiàn)在又是一個好時(shí)機(jī)來告訴你變更列表界面提供方便的分頁功能懂从。 默認(rèn)每頁顯示100條記錄蹲蒲。 Change listpagination
, search boxes
, filters
, date-hierarchies
, 和 column-header-ordering
都將按照你設(shè)想的那樣工作。
定制管理后臺的外觀
很明顯届搁,每個管理頁面的頂部都有“Django administration”不太合適。 它僅僅起到了占位符的作用卡睦。
它可以用Django的模板系統(tǒng)輕松改變。 Django的管理站點(diǎn)是用Django自己制作出來的表锻,它的界面代碼使用的是Django自己的模板系統(tǒng)。
定制項(xiàng)目的模板
在你項(xiàng)目的文件夾內(nèi)(包含 manage.py
的目錄)創(chuàng)建一個templates
目錄瞬逊。 Templates可以放在你的文件系統(tǒng)中的任何地方, 只要Django所能訪問到。(運(yùn)行Web服務(wù)器的用戶即是運(yùn)行Django的用戶)确镊。 然而,把模板放在項(xiàng)目目錄下會是一個值得提倡的蕾域、應(yīng)該遵循的約定。
打開你的配置文件(記住是mysite/settings.py
)在 TEMPLATES
settings中添加一個 DIRS
選項(xiàng):
mysite/settings.py
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模板時(shí)要檢查的文件系統(tǒng)目錄的列表巨缘;它是一個搜索路徑采呐。
安排模塊結(jié)構(gòu)
就像靜態(tài)文件一樣带猴,我們可以將所有的模板都放在一個大的模板目錄中懈万,并且工作得很好。但是会通,屬于特定應(yīng)用程序的模板應(yīng)該放在該應(yīng)用程序的模板目錄中(例如polls/templates
)而不是項(xiàng)目的(templates
)。 我們將在 reusable apps tutorial 中更詳細(xì)地討論為什么我們這樣做涕侈。
現(xiàn)在,在templates
下創(chuàng)建一個名為admin
的文件夾裳涛,然后從Django安裝的原目錄下(目錄為django/contrib/admin/templates
)將模板頁面的源文件admin/base_site.html
拷貝到這個文件夾里。
Django的源文件在哪里舷礼?
如果你找不到Django源文件在你系統(tǒng)上的位置,運(yùn)行如下命令:
python -c "import django; print(django.__path__)"
然后妻献,只需編輯該文件并替換{{ site_header|default:_('Django administration') }}
(包括花括號)為你認(rèn)為合適的自己站點(diǎn)的名稱。 編輯完成后應(yīng)該類似下面的代碼片段:
{% block branding %}
<h1 id="site-name"><a href="{% url 'admin:index' %}">Polls Administration</a></h1>
{% endblock %}
我們使用這個例子來教你如何覆蓋模板育拨。在實(shí)際項(xiàng)目中,你可能會使用 django.contrib.admin.AdminSite.site_header
屬性來更簡單地實(shí)現(xiàn)這個自定義功能熬丧。
模板文件包含許多類似{% block branding %}
和{{ title }}
這樣的文本。 {{
和{%
是Django模板語言的一部分析蝴。 當(dāng)Django呈現(xiàn)admin/base_site.html
時(shí),將會評估此模板語言以生成最終的HTML頁面嫌变,就像我們在Tutorial 3看到的那樣。
注意任何Django管理站點(diǎn)的默認(rèn)模板都可以被覆蓋腾啥。 想要覆蓋一個模板文件,只需要做和覆蓋base_site.html
相同的操作就行 —— 將它從默認(rèn)的目錄拷貝到你自定義的目錄中疮跑,然后修改它。
定制應(yīng)用的模板
See the template loading documentation for more information about how Django finds its templates.
細(xì)心的讀者將會問:由于DIRS
默認(rèn)是空的祖娘,Django是怎么找到默認(rèn)的管理站點(diǎn)模板的啊奄? 答案是渐苏,由于 APP_DIRS
設(shè)置為True
菇夸,Django會自動地在每個應(yīng)用包下面查找一個templates/
子目錄,留作備用庄新。(別忘了,django.contrib.admin
也是一個應(yīng)用)择诈。
我們的投票應(yīng)用并不是太復(fù)雜,不需要自定義管理站點(diǎn)模板羞芍。 但是如果它變得更加復(fù)雜而且為了一些功能需要修改Django的標(biāo)準(zhǔn)管理站點(diǎn)模板,那么與修改項(xiàng)目中的模板相比涩金,修改應(yīng)用中的模板將是更明智的選擇暇仲。 使用這種方式副渴,你可以在任何新項(xiàng)目中使用投票應(yīng)用,并且可以確保Django將找到它需要的自定義模板文件煮剧。
更多關(guān)于Django如何找到它的模板文件的信息将鸵,請查看 template loading documentation勉盅。
自定義管理后臺的主頁
與上面類似的是顶掉,你可能想自定義Django管理站點(diǎn)首頁面的外觀。
默認(rèn)情況下痒筒,首頁面顯示所有位于 INSTALLED_APPS
中且已經(jīng)使用管理站點(diǎn)應(yīng)用注冊過的應(yīng)用,這些應(yīng)用按照字母順序進(jìn)行顯示簿透。 你可能想在布局上做出重大改變。 畢竟葡盗,首頁面可能是管理站點(diǎn)中最重要的頁面,并且它應(yīng)當(dāng)易用觅够。
需要自定義的模板文件是 admin/index.html
巷嚣。 (與上一節(jié)中的admin/base_site.html
相同 - 將其從默認(rèn)目錄復(fù)制到您的自定義模板目錄)蔚约。 編輯這個文件涂籽,你將看到它有一個叫做app_list
的變量。 這個變量包含安裝的所有Django應(yīng)用评雌。 你可以選擇不像默認(rèn)模板中那樣使用它树枫,而是以你認(rèn)為最好的方式硬編碼鏈接到每個對象自己的管理頁面。
接下來是什么景东?
初學(xué)者教程結(jié)束于此砂轻。 在這期間,你可能想要在where to go from here中了解文檔的結(jié)構(gòu)和查找相關(guān)信息方法斤吐。
如果你熟悉Python 打包的技術(shù)搔涝,并且對如何將投票應(yīng)用制作成一個“可重用的應(yīng)用”感興趣厨喂,請看 Advanced tutorial: How to write reusable apps.