Django 教程第四部分:Django 管理站點
現在我們已經為 LocalLibrary 網站建立了模型,我們將使用 Django 管理站點來新增一些“真實”的圖書資料。首先,我們將向你展示如何將模型註冊到管理站點,然後我們將向你展示如何登入並建立一些資料。在文章的最後,我們將展示一些可以進一步改進管理站點呈現方式的方法。
| 預備知識 | 首先完成:Django 教程第三部分:使用模型。 |
|---|---|
| 目標 | 瞭解 Django 管理站點的優點和侷限性,並使用它為我們的模型建立一些記錄。 |
概述
Django 管理應用程式可以使用你的模型自動構建一個站點區域,你可以使用該區域建立、檢視、更新和刪除記錄。這可以為你節省大量的開發時間,使測試模型和了解你是否有正確資料變得非常容易。管理應用程式對於生產中的資料管理也很有用,具體取決於網站的型別。Django 專案建議僅將其用於內部資料管理(即,僅供管理員或組織內部人員使用),因為以模型為中心的方法不一定是所有使用者的最佳介面,並且暴露了模型中許多不必要的細節。
在你建立骨架專案時,包含管理應用程式到你的網站所需的所有配置都已自動完成(有關實際所需依賴項的資訊,請參閱此處 Django 文件)。因此,你必須做的只是將模型註冊到管理應用程式。在本文的最後,我們將簡要演示如何進一步配置管理區域以更好地顯示我們的模型資料。
註冊模型後,我們將展示如何建立新的“超級使用者”,登入站點,並建立一些圖書、作者、圖書例項和型別。這些將有助於測試我們在下一教程中開始建立的檢視和模板。
註冊模型
首先,在 catalog 應用程式中開啟 admin.py (/django-locallibrary-tutorial/catalog/admin.py)。它目前看起來是這樣的 — 請注意它已經匯入了 django.contrib.admin
from django.contrib import admin
# Register your models here.
透過將以下文字複製到檔案底部來註冊模型。此程式碼匯入模型,然後呼叫 admin.site.register 來註冊它們。
from .models import Author, Genre, Book, BookInstance, Language
admin.site.register(Book)
admin.site.register(Author)
admin.site.register(Genre)
admin.site.register(BookInstance)
admin.site.register(Language)
注意:上面的行假定你接受了建立表示圖書自然語言的模型的挑戰(請參閱模型教程文章)!
這是向站點註冊模型的最簡單方法。管理站點是高度可定製的,我們將在後面更詳細地討論註冊模型的其他方法。
建立超級使用者
為了登入管理站點,我們需要一個啟用了員工狀態的使用者帳戶。為了檢視和建立記錄,我們還需要此使用者擁有管理我們所有物件的許可權。你可以使用 manage.py 建立一個對站點具有完全訪問許可權和所有必要許可權的“超級使用者”帳戶。
在 manage.py 所在的同一目錄中,呼叫以下命令建立超級使用者。系統將提示你輸入使用者名稱、電子郵件地址和強密碼。
python3 manage.py createsuperuser
此命令完成後,一個新的超級使用者將被新增到資料庫中。現在重新啟動開發伺服器,以便我們可以測試登入
python3 manage.py runserver
登入並使用站點
要登入站點,請開啟 /admin URL(例如,http://127.0.0.1:8000/admin)並輸入你的新超級使用者 ID 和密碼憑據(你將被重定向到登入頁面,然後在輸入詳細資訊後返回到 /admin URL)。
站點的這一部分顯示了我們所有的模型,按已安裝的應用程式分組。你可以單擊模型名稱以轉到列出其所有相關記錄的螢幕,並且可以進一步單擊這些記錄進行編輯。你還可以直接單擊每個模型旁邊的新增連結,以開始建立該型別的記錄。

單擊圖書右側的新增連結以建立一本新書(這將顯示一個類似下面對話方塊)。請注意,每個欄位的標題、使用的控制元件型別以及 help_text(如果有)如何與你在模型中指定的值匹配。
輸入欄位的值。你可以透過按相應欄位旁邊的+按鈕來建立新的作者或型別(如果已經建立了,也可以從列表中選擇現有值)。完成後,你可以按儲存、儲存並新增另一個或儲存並繼續編輯來儲存記錄。

注意:此時,我們希望你花一些時間嚮應用程式新增一些圖書、作者、語言和型別(例如,幻想)。確保每個作者和型別都包含幾本不同的圖書(這將在我們稍後在系列文章中實現列表和詳細檢視時使它們更有趣)。
完成新增圖書後,單擊頂部書籤中的主頁連結返回主管理頁面。然後單擊圖書連結以顯示當前圖書列表(或單擊其他連結以檢視其他模型列表)。現在你已經添加了一些圖書,列表可能類似於下面的螢幕截圖。顯示了每本書的標題;這是我們在上一篇文章中指定的 Book 模型的 __str__() 方法返回的值。

在此列表中,你可以透過選擇不需要的圖書旁邊的複選框,從操作下拉列表中選擇刪除…操作,然後按Go按鈕來刪除圖書。你還可以透過按新增圖書按鈕來新增新圖書。
你可以透過選擇連結中的名稱來編輯圖書。下面顯示的圖書編輯頁面幾乎與“新增”頁面相同。主要區別在於頁面標題(更改圖書)和刪除、歷史和在網站上檢視按鈕的新增(最後一個按鈕出現是因為我們在模型中定義了 get_absolute_url() 方法)。
注意:單擊在網站上檢視按鈕會引發 NoReverseMatch 異常,因為 get_absolute_url() 方法嘗試 reverse() 尚未定義的命名 URL 對映(“book-detail”)。我們將在Django 教程第六部分:通用列表和詳細檢視中定義 URL 對映和相關檢視。

現在導航回主頁(使用麵包屑導航中的主頁連結),然後檢視作者和型別列表 — 你在新增新書時應該已經建立了相當多的內容,但請隨意新增更多。
你不會擁有任何圖書例項,因為它們不是從圖書建立的(儘管你可以從 BookInstance 建立 Book — 這是 ForeignKey 欄位的性質)。導航回主頁並按相關新增按鈕以顯示下面的新增圖書例項螢幕。請注意大型、全域性唯一的 ID,它可用於在圖書館中單獨識別一本書的單個副本。

為你的每本書建立多個這些記錄。將至少一些記錄的狀態設定為可用,將其他記錄的狀態設定為借出中。如果狀態不是可用,則還要設定未來的歸還日期。
就是這樣!你現在已經學會了如何設定和使用管理站點。你還為 Book、BookInstance、Genre、Language 和 Author 建立了記錄,一旦我們建立了自己的檢視和模板,我們就可以使用這些記錄。
高階配置
Django 很好地利用註冊模型中的資訊建立了一個基本的管理站點
- 每個模型都有一個由模型
__str__()方法建立的字串標識的單個記錄列表,並連結到用於編輯的詳細檢視/表單。預設情況下,此檢視頂部有一個操作選單,你可以使用該選單對記錄執行批次刪除操作。 - 用於編輯和新增記錄的模型詳細記錄表單包含模型中的所有欄位,按其宣告順序垂直佈局。
你可以進一步自定義介面,使其更易於使用。你可以做的一些事情是
-
列表檢視
- 為每個記錄新增額外的欄位/資訊。
- 新增過濾器以根據日期或其他選擇值(例如,圖書借閱狀態)選擇要列出的記錄。
- 向列表檢視中的操作選單新增額外的選項,並選擇此選單在表單上的顯示位置。
-
詳細檢視
- 選擇要顯示(或排除)的欄位,以及它們的順序、分組、是否可編輯、使用的控制元件、方向等。
- 向記錄新增相關欄位以允許內聯編輯(例如,在你建立作者記錄時新增新增和編輯圖書記錄的功能)。
在本節中,我們將介紹一些將改進我們的 LocalLibrary 介面的更改,包括向 Book 和 Author 模型列表新增更多資訊,並改進其編輯檢視的佈局。我們不會更改 Language 和 Genre 模型的呈現,因為它們每個只有一個欄位,因此這樣做沒有實際好處!
你可以在Django Admin site(Django 文件)中找到所有管理站點自定義選項的完整參考。
註冊 ModelAdmin 類
要更改模型在管理介面中的顯示方式,你需要定義一個 ModelAdmin 類(描述佈局)並將其註冊到模型。
讓我們從 Author 模型開始。在 catalog 應用程式中開啟 admin.py (/django-locallibrary-tutorial/catalog/admin.py)。註釋掉你為 Author 模型進行的原始註冊(在其前面加上 #)
# admin.site.register(Author)
現在新增一個新的 AuthorAdmin 和註冊,如下所示。
# Define the admin class
class AuthorAdmin(admin.ModelAdmin):
pass
# Register the admin class with the associated model
admin.site.register(Author, AuthorAdmin)
現在我們將為 Book 和 BookInstance 新增 ModelAdmin 類。我們再次需要註釋掉原始註冊
# admin.site.register(Book)
# admin.site.register(BookInstance)
現在建立並註冊新模型;為了本演示的目的,我們將改用 @register 裝飾器來註冊模型(這與 admin.site.register() 語法完全相同)
# Register the Admin classes for Book using the decorator
@admin.register(Book)
class BookAdmin(admin.ModelAdmin):
pass
# Register the Admin classes for BookInstance using the decorator
@admin.register(BookInstance)
class BookInstanceAdmin(admin.ModelAdmin):
pass
目前,我們所有的管理類都是空的(請參見 pass),因此管理行為將保持不變!我們現在可以擴充套件這些類來定義模型特定的管理行為。
配置列表檢視
LocalLibrary 目前使用從模型 __str__() 方法生成的物件名稱列出所有作者。當你只有少數作者時,這很好,但一旦你有許多作者,你可能會遇到重複。為了區分它們,或者僅僅因為你想顯示每個作者更多有趣的資訊,你可以使用 list_display 將附加欄位新增到檢視中。
用下面的程式碼替換你的 AuthorAdmin 類。列表中要顯示的欄位按所需順序在元組中宣告,如所示(這些與你的原始模型中指定的名稱相同)。
class AuthorAdmin(admin.ModelAdmin):
list_display = ('last_name', 'first_name', 'date_of_birth', 'date_of_death')
現在導航到你網站中的作者列表。上面欄位現在應該顯示,如下所示

對於我們的 Book 模型,我們還將顯示 author 和 genre。author 是一個 ForeignKey 欄位(一對多)關係,因此將由關聯記錄的 __str__() 值表示。用下面的版本替換 BookAdmin 類。
class BookAdmin(admin.ModelAdmin):
list_display = ('title', 'author', 'display_genre')
不幸的是,我們不能直接在 list_display 中指定 genre 欄位,因為它是一個 ManyToManyField(Django 阻止這樣做是因為這樣做會有很大的資料庫訪問“成本”)。相反,我們將定義一個 display_genre 函式來獲取資訊作為字串(這是我們上面呼叫的函式;我們將在下面定義它)。
注意:在此處獲取 genre 可能不是一個好主意,因為資料庫操作的“成本”。我們之所以向你展示,是因為在你的模型中呼叫函式在其他原因下可能非常有用——例如,在列表中的每個專案旁邊新增一個刪除連結。
將以下程式碼新增到你的 Book 模型 (models.py) 中。這將從 genre 欄位的前三個值(如果存在)建立一個字串,併為此方法建立一個可在管理站點中使用的 short_description。
def display_genre(self):
"""Create a string for the Genre. This is required to display genre in Admin."""
return ', '.join(genre.name for genre in self.genre.all()[:3])
display_genre.short_description = 'Genre'
儲存模型並更新管理後,開啟你的網站並轉到圖書列表頁面;你應該看到一個如下所示的圖書列表

Genre 模型(以及 Language 模型,如果你定義了一個)都只有一個欄位,因此沒有必要為它們建立額外的模型來顯示額外的欄位。
注意:值得更新 BookInstance 模型列表以至少顯示狀態和預期歸還日期。我們已將其作為本文末尾的一個挑戰!
新增列表過濾器
一旦列表中有很多專案,能夠過濾顯示哪些專案會很有用。這是透過在 list_filter 屬性中列出欄位來完成的。用下面的程式碼片段替換你當前的 BookInstanceAdmin 類。
class BookInstanceAdmin(admin.ModelAdmin):
list_filter = ('status', 'due_back')
列表檢視現在將包含一個位於右側的過濾器框。請注意如何選擇日期和狀態來過濾值

組織詳細檢視佈局
預設情況下,詳細檢視垂直佈局所有欄位,按其在模型中的宣告順序。你可以更改宣告順序、顯示(或排除)的欄位、是否使用節來組織資訊、欄位是水平還是垂直顯示,甚至是在管理表單中使用哪些編輯控制元件。
注意:LocalLibrary 模型相對簡單,所以我們沒有很大的必要更改佈局;不過,我們還是會做一些更改,只是為了向你展示如何操作。
控制哪些欄位顯示和佈局
更新你的 AuthorAdmin 類以新增 fields 行,如下所示
class AuthorAdmin(admin.ModelAdmin):
list_display = ('last_name', 'first_name', 'date_of_birth', 'date_of_death')
fields = ['first_name', 'last_name', ('date_of_birth', 'date_of_death')]
fields 屬性僅列出要在表單上顯示的欄位,按順序排列。欄位預設垂直顯示,但如果你將它們進一步分組到元組中(如上面“日期”欄位所示),則會水平顯示。
在你的網站中,轉到作者詳細檢視 — 它現在應該顯示如下

注意:你還可以使用 exclude 屬性宣告要從表單中排除的屬性列表(模型中的所有其他屬性都將顯示)。
詳細檢視分段
你可以使用 fieldsets 屬性在詳細表單中新增“節”以分組相關模型資訊。
在 BookInstance 模型中,我們有與圖書相關的資訊(即 name、imprint 和 id)以及何時可用(status、due_back)。我們可以將它們新增到我們的 BookInstanceAdmin 類中,如下所示,使用 fieldsets 屬性。
@admin.register(BookInstance)
class BookInstanceAdmin(admin.ModelAdmin):
list_filter = ('status', 'due_back')
fieldsets = (
(None, {
'fields': ('book', 'imprint', 'id')
}),
('Availability', {
'fields': ('status', 'due_back')
}),
)
每個節都有自己的標題(如果你不需要標題,則為 None)和一個關聯的欄位元組在字典中 — 格式描述起來很複雜,但如果你檢視上面的程式碼片段,則很容易理解。
現在導航到你網站中的圖書例項檢視;表單應該顯示如下

關聯記錄的內聯編輯
有時能夠同時新增關聯記錄是有意義的。例如,在同一詳細頁面上同時擁有圖書資訊和有關你擁有的特定副本的資訊可能是有意義的。
你可以透過宣告 inlines,型別為 TabularInline(水平佈局)或 StackedInline(垂直佈局,就像預設的模型佈局)來實現這一點。你可以透過在 BookAdmin 中指定 inlines 來將 BookInstance 資訊內聯到我們的 Book 詳細資訊中
class BooksInstanceInline(admin.TabularInline):
model = BookInstance
@admin.register(Book)
class BookAdmin(admin.ModelAdmin):
list_display = ('title', 'author', 'display_genre')
inlines = [BooksInstanceInline]
現在導航到你網站中某個 Book 的檢視 — 在底部,你應該會看到與這本書相關的圖書例項(緊鄰圖書的型別欄位下方)

在這種情況下,我們所做的只是宣告我們的表格內聯類,它只新增內聯模型中的所有欄位。你可以為佈局指定各種附加資訊,包括要顯示的欄位、它們的順序、它們是否只讀等(有關更多資訊,請參閱TabularInline)。
注意:此功能有一些令人頭痛的限制!在上面的螢幕截圖中,我們有三個現有的圖書例項,然後是三個新圖書例項的佔位符(它們看起來非常相似!)。預設情況下沒有備用圖書例項,只需使用新增另一個圖書例項連結新增它們,或者能夠將 BookInstances 列為非可讀連結,這樣會更好。第一個選項可以透過在 BooksInstanceInline 模型中將 extra 屬性設定為 0 來完成,你可以自己嘗試一下。
挑戰自我
我們在這部分學到了很多東西,現在是時候嘗試一些事情了。
- 對於
BookInstance列表檢視,新增程式碼以顯示圖書、狀態、歸還日期和 ID(而不是預設的__str__()文字)。 - 使用與我們對
Book/BookInstance相同的方法,將Book項的內聯列表新增到Author詳細檢視中。
總結
就是這樣!你現在已經學會了如何以最簡單和改進的形式設定管理站點,如何建立超級使用者,以及如何導航管理站點並檢視、刪除和更新記錄。在此過程中,你建立了一批圖書、圖書例項、型別和作者,一旦我們建立了自己的檢視和模板,我們就可以列出和顯示它們。
延伸閱讀
- 編寫你的第一個 Django 應用,第二部分:介紹 Django Admin(Django 文件)
- Django Admin site(Django 文件)