Django 亮點:模型、管理和利用關係數據庫(第 3 部分)
已發表: 2022-03-10在我們開始之前,我想指出 Django 的內置管理功能,即使在定制之後,也不適合最終用戶。 管理面板作為開發人員、操作員和管理員工具存在,用於創建和維護軟件。 它的目的不是為最終用戶提供審核功能或您開發的平台上的任何其他管理員功能。
本文基於兩個部分的假設:
- Django 管理面板非常直觀,您基本上已經知道如何使用它。
- Django 管理面板非常強大,我們可以將其用作學習使用 Django 模型在關係數據庫中表示數據的工具。
我提出這些想法的一個警告是,我們仍然需要編寫一些配置代碼來激活管理面板更強大的功能,並且我們仍然需要使用 Django 的基於模型的 ORM(對象關係映射)來指定數據的表示在我們的系統中。
推薦閱讀
“Django Highlights”是一個介紹 Django Web 開發重要概念的系列。 您可能想閱讀有關提供安全用戶身份驗證流程的內容,並關注使用 Django 模板編寫複雜頁面的演示。
配置
我們將在本文中使用一個示例項目。 該項目對圖書館將存儲的有關其書籍和顧客的一些數據進行建模。 該示例應該相當適用於管理用戶和/或庫存的許多類型的系統。 以下是數據的外觀:
請完成以下步驟以使示例代碼在您的本地計算機上運行。
1. 安裝包
安裝 Python 3.6 或更高版本後,創建目錄和虛擬環境。 然後,安裝以下軟件包:
pip install django django-grappelli
Django 是我們在本文中使用的 Web 框架。 ( django-grappelli
是一個我們將簡要介紹的管理面板主題。)
2. 獲得項目
安裝之前的包後,從 GitHub 下載示例代碼。 跑步:
git clone https://github.com/philipkiely/library_records.git cd library_records/library
3. 創建超級用戶
使用以下命令,設置您的數據庫並創建一個超級用戶。 命令行界面將引導您完成創建超級用戶的過程。 您的超級用戶帳戶將成為您訪問管理面板的方式,因此請務必記住您設置的密碼。 採用:
python manage.py migrate python manage.py createsuperuser
4. 加載數據
為了我們的探索,我創建了一個稱為夾具的數據集,您可以將其加載到數據庫中(有關如何創建夾具的更多信息,請參見文章末尾)。 在管理面板中瀏覽數據庫之前,請使用夾具填充您的數據庫。 跑步:
python manage.py loaddata ../fixture.json
5. 運行示例項目
最後,您已準備好運行示例代碼。 要運行服務器,請使用以下命令:
python manage.py runserver
打開瀏覽器到 https://127.0.0.1:8000 查看項目。 請注意,您會自動重定向到位於/admin/的管理面板。 我通過library/urls.py中的以下配置實現了這一點:
from django.contrib import admin from django.urls import path from records import views urlpatterns = [ path('admin/', admin.site.urls), path('', views.index), ]
結合records/views.py中的以下簡單重定向:
from django.http import HttpResponseRedirect def index(request): return HttpResponseRedirect('/admin/')
使用管理面板
我們成功了! 加載頁面時,您應該會看到如下內容:
此視圖是使用records/admin.py中的以下樣板代碼完成的:
from django.contrib import admin from .models import Book, Patron, Copy admin.site.register(Book) admin.site.register(Copy) admin.site.register(Patron)
該視圖應該讓您初步了解系統存儲的數據。 我將揭開一些謎團: Groups
和Users
由 Django 定義,並存儲系統上帳戶的信息和權限。 您可以在本系列的前一篇文章中閱讀有關User
模型的更多信息。 Books
、 Copys
和Patrons
是我們在運行遷移時創建並通過加載夾具填充的數據庫中的表。 請注意,Django 通過附加“s”天真地使模型名稱複數,即使在拼寫錯誤的“副本”等情況下也是如此。
在我們的項目中, Book
是包含標題、作者、出版日期和 ISBN(國際標準書號)的記錄。 圖書館維護每Book
的Copy
,或者可能是多個。 每個Copy
都可以由Patron
簽出,或者當前可以簽入Patron
是User
的擴展,記錄了他們的地址和出生日期。
創建、讀取、更新、銷毀
管理面板的一項標準功能是添加每個模型的實例。 點擊“書籍”進入模型頁面,點擊右上角的“添加書籍”按鈕。 這樣做會拉出一個表格,您可以填寫並保存以創建一本書。
創建Patron
揭示了管理員創建表單的另一個內置功能:您可以直接從同一表單創建連接模型。 下面的屏幕截圖顯示了由User
下拉右側的綠色加號觸發的彈出窗口。 因此,您可以在同一個管理頁面上創建兩個模型。
您可以通過相同的機制創建COPY
。
對於每條記錄,您可以單擊該行以使用相同的表單對其進行編輯。 您還可以使用管理員操作刪除記錄。
管理員操作
雖然管理面板的內置功能非常有用,但您可以使用管理操作創建自己的工具。 我們將創建兩個:一個用於創建書籍副本,另一個用於簽入已歸還圖書館的書籍。
要創建Book
Copy
,請轉到 URL /admin/records/book/
並使用“操作”下拉菜單選擇“添加書籍副本”,然後使用左側列上的複選框以選擇將哪本書或哪些書籍添加到庫存中。
創建它依賴於我們稍後將介紹的模型方法。 我們可以通過在records/admin.py中為Profile
模型創建一個ModelAdmin
類來將其稱為管理操作,如下所示:
from django.contrib import admin from .models import Book, Patron, Copy class BookAdmin(admin.ModelAdmin): list_display = ("title", "author", "published") actions = ["make_copys"] def make_copys(self, request, queryset): for q in queryset: q.make_copy() self.message_user(request, "copy(s) created") make_copys.short_description = "Add a copy of book(s)" admin.site.register(Book, BookAdmin)
list_display
屬性表示在模型的概覽頁面中使用哪些字段來表示模型。 actions
屬性列出了管理員操作。 我們的管理操作被定義為BookAdmin
中的一個函數,並接受三個參數:管理對象本身、請求(客戶端發送的實際 HTTP 請求)和查詢集(選中框的對象列表)。 我們對查詢集中的每個項目執行相同的操作,然後通知用戶操作已完成。 每個管理員操作都需要簡短的描述,以便可以在下拉菜單中正確識別。 最後,我們現在在註冊模型時添加BookAdmin
。
為批量設置屬性編寫管理操作非常重複。 這是簽入Copy
的代碼,請注意它與上一個操作幾乎等價。
from django.contrib import admin from .models import Book, Patron, Copy class CopyAdmin(admin.ModelAdmin): actions = ["check_in_copys"] def check_in_copys(self, request, queryset): for q in queryset: q.check_in() self.message_user(request, "copy(s) checked in") check_in_copys.short_description = "Check in copy(s)" admin.site.register(Copy, CopyAdmin)
管理主題
默認情況下,Django 為管理面板提供了相當簡單的樣式。 您可以創建自己的主題或使用第三方主題來讓管理面板煥然一新。 一個流行的開源主題是 grappelli,我們在本文前面安裝了它。 您可以查看文檔以了解其全部功能。
安裝主題非常簡單,只需要兩行。 首先,在library/settings.py中將grappelli
添加到INSTALLED_APPS
中,如下所示:
INSTALLED_APPS = [ 'grappelli', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'records', ]
然後,調整library/urls.py :
from django.contrib import admin from django.urls import path, include from records import views urlpatterns = [ path('grappelli/', include('grappelli.urls')), path('admin/', admin.site.urls), path('', views.index), ]
完成這些更改後,管理面板應如下所示:
還有許多其他主題,您可以再次開發自己的主題。 在本文的其餘部分,我將堅持使用默認外觀。
了解模型
現在您已經熟悉了管理面板並使用它來導航數據,讓我們來看看定義我們的數據庫結構的模型。 每個模型代表關係數據庫中的一個表。
關係數據庫將數據存儲在一個或多個表中。 這些表中的每一個都有一個指定的列結構,包括一個主鍵(每個元素的唯一標識符)和一列或多列值,這些值有各種類型,如字符串、整數和日期。 存儲在數據庫中的每個對像都表示為一行。 名稱的“關係”部分來自可以說是該技術最重要的特性:在表之間創建關係。 對象(行)可以與其他表中的行進行一對一、一對多(外鍵)或多對多映射。 我們將在示例中進一步討論這一點。
默認情況下,Django 使用 SQLite3 進行開發。 SQLite3 是一個簡單的關係數據庫引擎,當您第一次運行python manage.py migrate
時,您的數據庫會自動創建為db.sqlite3 。 我們將在本文中繼續使用 SQLite3,但它不適合生產使用,主要是因為並髮用戶可能會覆蓋。 在生產環境中,或者在編寫您打算部署的系統時,請使用 PostgreSQL 或 MySQL。
Django 使用模型與數據庫交互。 使用 Django 的 ORM 的一部分, records/models.py文件包含多個模型,它允許為每個對象指定字段、屬性和方法。 在創建模型時,我們力求在合理範圍內實現“胖模型”架構。 這意味著應在模型本身的規範中處理盡可能多的數據驗證、解析、處理、業務邏輯、異常處理、邊緣案例解決和類似任務。 在底層,Django 模型是非常複雜、功能豐富的對象,具有廣泛有用的默認行為。 這使得即使不編寫大量代碼也可以輕鬆實現“胖模型”架構。
讓我們來看看示例應用程序中的三個模型。 我們無法涵蓋所有內容,因為這應該是一篇介紹性文章,而不是 Django 框架的完整文檔,但我將重點介紹我在構建這些簡單模型時所做的最重要的選擇。
Book
類是模型中最直接的。 這是來自records/models.py :
from django.db import models class Book(models.Model): title = models.CharField(max_length=300) author = models.CharField(max_length=150) published = models.DateField() isbn = models.IntegerField(unique=True) def __str__(self): return self.title + " by " + self.author def make_copy(self): Copy.objects.create(book=self)
所有CharField
字段都需要指定的max_length
屬性。 常規長度為 150 個字符,如果標題很長,我將其加倍作為title
。 當然,仍然有一個任意的限制,可以超過。 對於無限制的文本長度,請使用TextField
。 published
的字段是DateField
。 這本書的出版時間無關緊要,但如果確實如此,我會使用DateTimeField
。 最後,ISBN 是一個整數(ISBN 是 10 位或 13 位數字,因此都符合整數的最大值),我們使用unique=True
因為沒有兩本書可以具有相同的 ISBN,然後在數據庫級別強制執行。
所有對像都有一個定義其字符串表示的方法__str__(self)
。 我們覆蓋了models.Model
類提供的默認實現,而是在模型將被表示為字符串的所有地方都將書籍表示為“作者的標題”。 回想一下,之前我們在Book
的管理對像中使用list_display
來確定管理面板列表中將顯示哪些字段。 如果該list_display
不存在,則管理列表將顯示模型的字符串表示形式,就像對Patron
和Copy
一樣。
最後,我們在Book
上有一個方法,我們在之前編寫的管理操作中調用了該方法。 這個函數創建一個與數據庫中給定的Book
實例相關的Copy
。
繼續討論Patron
,這個模型引入了一對一關係的概念,在這個例子中是內置的User
模型。 從records/models.py 中查看:
from django.db import models from django.contrib.auth.models import User class Patron(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) address = models.CharField(max_length=150) dob = models.DateField() def __str__(self): return self.user.username
user
字段不完全是雙射函數。 可以有一個沒有關聯的Patron
實例的User
實例。 但是,一個User
不能與一個以上的Patron
實例相關聯,並且如果沒有與用戶的確切關係,一個Patron
就不能存在。 這是在數據庫級別強制執行的,並且由on_delete=models.CASCADE
規範保證:如果刪除User
實例,則將刪除關聯的Profile
文件。
我們之前見過的其他字段和__str__(self)
函數。 值得注意的是,您可以通過一對一的關係來獲取模型函數中的屬性,在本例中為user.username
。
為了擴展數據庫關係的用處,讓我們將注意力轉向從records/models.py Copy
:
from django.db import models class Copy(models.Model): book = models.ForeignKey(Book, on_delete=models.CASCADE) out_to = models.ForeignKey(Patron, blank=True, null=True, on_delete=models.SET_NULL) def __str__(self): has_copy = "checked in" if self.out_to: has_copy = self.out_to.user.username return self.book.title + " -> " + has_copy def check_out(self, p): self.out_to = p self.save() def check_in(self): self.out_to = None self.save()
同樣,我們之前已經看過大部分內容,所以讓我們關注新的東西: models.ForeignKey
。 一個Copy
必須是一個Book
,但圖書館可能有每Book
的多個Copy
。 Book
可以存在於數據庫中,而圖書館的目錄中沒有Copy
,但如果沒有基礎Book
,則Copy
不能存在。
這種複雜的關係用下面這行來表示:
book = models.ForeignKey(Book, on_delete=models.CASCADE)
刪除行為與Patron
對User
的引用相同。
Copy
和Patron
之間的關係略有不同。 一個Copy
最多可以被一個Patron
簽出,但是每個Patron
可以簽出盡可能多的Copy
,只要圖書館允許他們。 但是,這不是永久的關係,有時不會簽出Copy
。 Patron
和Copy
在數據庫中相互獨立存在; 刪除一個實例不應刪除另一個實例。
這種關係仍然是外鍵的一個用例,但有不同的參數:
out_to = models.ForeignKey(Patron, blank=True, null=True, on_delete=models.SET_NULL)
在這裡,具有blank=True
允許表單接受None
作為關係的值,而null=True
允許數據庫中Copy
表中的Patron
關係的列接受null
作為值。 如果在簽出該Copy
時刪除了Patron
實例,則會在Copy
上觸發刪除行為,該行為是通過將Patron
字段設置為 null 來切斷關係,同時保持Copy
不變。
相同的字段類型models.ForeignKey
可以表達對象之間非常不同的關係。 在這個例子中我無法完全適應的一個關係是一個多對多字段,它就像一個一對一的字段,除了,正如它的名字所暗示的那樣,每個實例都可以與許多其他實例相關聯並且每個人都可以與許多其他人相關聯,例如一本書如何有多個作者,每個作者都寫過多本書。
遷移
您可能想知道數據庫如何知道模型中表達的內容。 根據我的經驗,遷移是非常簡單的事情之一,直到它們不是,然後它們會吃掉你的臉。 對於初學者,以下是保持杯子完好無損的方法:了解遷移以及如何與它們交互,但盡量避免對遷移文件進行手動編輯。 如果您已經知道自己在做什麼,請跳過本節並繼續了解適合您的方法。
無論哪種方式,請查看官方文檔以獲得對該主題的完整處理。
遷移將模型中的更改轉換為數據庫模式中的更改。 您不必自己編寫它們,Django 使用python manage.py makemigrations
命令創建它們。 您應該在創建新模型或編輯現有模型的字段時運行此命令,但在創建或編輯模型方法時無需這樣做。 重要的是要注意遷移以鏈的形式存在,每個遷移都引用前一個遷移,以便可以對數據庫模式進行無錯誤的編輯。 因此,如果您在一個項目上進行協作,那麼在版本控制中保持一個一致的遷移歷史記錄是很重要的。 當有未應用的遷移時,運行python manage.py migrate
在運行服務器之前應用它們。
示例項目與單個遷移一起分發, records/migrations/0001_initial.py 。 同樣,這是自動生成的代碼,您不必編輯,所以我不會在這裡複製它,但是如果您想了解幕後發生的事情,請繼續看看它。
夾具
與遷移不同,固定裝置不是 Django 開發的常見方面。 我使用它們將樣本數據與文章一起分發,並且從未使用過它們。 但是,因為我們之前使用過一個,所以我不得不介紹這個主題。
這一次,官方文檔在這個話題上有點苗條。 總的來說,您應該知道的是,fixture 是一種從數據庫中導入和導出各種格式數據的方法,包括我使用的 JSON。 此功能主要用於幫助進行自動化測試等工作,而不是備份系統或在實時數據庫中編輯數據的方式。 此外,fixture 不會隨著遷移而更新,如果您嘗試將fixture 應用於具有不兼容模式的數據庫,它將失敗。
要為整個數據庫生成一個夾具,請運行:
python manage.py dumpdata --format json > fixture.json
要加載夾具,請運行:
python manage.py loaddata fixture.json
結論
在 Django 中編寫模型是一個很大的話題,而使用管理面板則是另一個話題。 3000字,我只介紹了每一個。 希望使用管理面板為您提供了更好的界面來探索模型如何工作和相互關聯,讓您有信心試驗和開發自己的數據關係表示。
如果您正在尋找一個簡單的起點,請嘗試添加一個像Profile
一樣從User
繼承的Librarian
模型。 對於更多挑戰,請嘗試為每個Copy
和/或Patron
實施結帳歷史記錄(有幾種方法可以完成此操作)。
Django Highlights 是一個介紹 Django 中 Web 開發的重要概念的系列。 每篇文章都是作為 Django 開發方面的獨立指南編寫的,旨在幫助前端開發人員和設計人員更深入地了解代碼庫的“另一半”。 這些文章主要是為了幫助您了解理論和約定,但包含一些用 Django 3.0 編寫的代碼示例。
延伸閱讀
您可能對以下文章和文檔感興趣。
- Django 亮點:用戶模型和身份驗證(第 1 部分)
- Django 亮點:模板化保存行(第 2 部分)
- Django 亮點:處理靜態資產和媒體文件(第 4 部分)
- Django 管理文檔
- Django 模型
- Django 模型字段參考
- Django 遷移