Python 100 Days Day42 Django Advance
20 Sep 2022 | beginner pythonauthor: jackfrued
深入模型
在上一個章節中,我們提到了Django是基於MVC架構的Web框架,MVC架構追求的是“模型”和“視圖”的解耦合。所謂“模型”說得更直白一些就是數據(的表示),所以通常也被稱作“數據模型”。在實際的項目中,數據模型通常通過數據庫實現持久化操作,而關系型數據庫在過去和當下都是持久化的首選方案,下面我們通過完成一個投票項目來講解和模型相關的知識點。投票項目的首頁會展示某在線教育平台所有的學科;點擊學科可以查看到該學科的老師及其信息;用戶登錄後在查看老師的頁面為老師投票,可以投讚成票和反對票;未登錄的用戶可以通過登錄頁進行登錄;尚未注冊的用戶可以通過注冊頁輸入個人信息進行注冊。在這個項目中,我們使用MySQL數據庫來實現數據持久化操作。
創建項目和應用
我們首先創建Django項目vote並為其添加虛擬環境和依賴項。接下來,在項目下創建名為polls的應用和保存模板頁的文件夾tempaltes,項目文件夾的結構如下所示。

根據上面描述的項目需求,我們準備了四個靜態頁面,分別是展示學科的頁面subjects.html,顯示學科老師的頁面teachers.html,登錄頁面login.html,注冊頁面register.html,稍後我們會將靜態頁修改為Django項目所需的模板頁。
配置關系型數據庫MySQL
-
在MySQL中創建數據庫,創建用戶,授權用戶訪問該數據庫。
create database vote default charset utf8; create user 'hellokitty'@'%' identified by 'Hellokitty.618'; grant all privileges on vote.* to 'hellokitty'@'%'; flush privileges; -
在MySQL中創建保存學科和老師信息的二維表(保存用戶信息的表稍後處理)。
use vote; -- 創建學科表 create table `tb_subject` ( `no` integer auto_increment comment '學科編號', `name` varchar(50) not null comment '學科名稱', `intro` varchar(1000) not null default '' comment '學科介紹', `is_hot` boolean not null default 0 comment '是不是熱門學科', primary key (`no`) ); -- 創建老師表 create table `tb_teacher` ( `no` integer auto_increment comment '老師編號', `name` varchar(20) not null comment '老師姓名', `sex` boolean not null default 1 comment '老師性別', `birth` date not null comment '出生日期', `intro` varchar(1000) not null default '' comment '老師介紹', `photo` varchar(255) not null default '' comment '老師照片', `gcount` integer not null default 0 comment '好評數', `bcount` integer not null default 0 comment '差評數', `sno` integer not null comment '所屬學科', primary key (`no`), foreign key (`sno`) references `tb_subject` (`no`) ); -
在虛擬環境中安裝連接MySQL數據庫所需的依賴項。
pip install mysqlclient說明:如果因為某些原因無法安裝
mysqlclient三方庫,可以使用它的替代品pymysql,pymysql是用純Python開發的連接MySQL的Python庫,安裝更容易成功,但是需要在Django項目文件夾的__init__.py中添加如下所示的代碼。import pymysql pymysql.install_as_MySQLdb()如果使用Django 2.2及以上版本,還會遇到PyMySQL跟Django框架的兼容性問題,兼容性問題會導致項目無法運行,需要按照GitHub上PyMySQL倉庫Issues中提供的方法進行處理。總體來說,使用
pymysql會比較麻煩,強烈建議大家首選安裝mysqlclient。 -
修改項目的settings.py文件,首先將我們創建的應用
polls添加已安裝的項目(INSTALLED_APPS)中,然後配置MySQL作為持久化方案。INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'polls', ] DATABASES = { 'default': { # 數據庫引擎配置 'ENGINE': 'django.db.backends.mysql', # 數據庫的名字 'NAME': 'vote', # 數據庫服務器的IP地址(本機可以寫localhost或127.0.0.1) 'HOST': 'localhost', # 啟動MySQL服務的端口號 'PORT': 3306, # 數據庫用戶名和口令 'USER': 'hellokitty', 'PASSWORD': 'Hellokitty.618', # 數據庫使用的字符集 'CHARSET': 'utf8', # 數據庫時間日期的時區設定 'TIME_ZONE': 'Asia/Chongqing', } }在配置ENGINE屬性時,常用的可選值包括:
'django.db.backends.sqlite3':SQLite嵌入式數據庫。'django.db.backends.postgresql':BSD許可證下發行的開源關系型數據庫產品。'django.db.backends.mysql':甲骨文公司經濟高效的數據庫產品。'django.db.backends.oracle':甲骨文公司關系型數據庫旗艦產品。
其他的配置可以參考官方文檔中數據庫配置的部分。
-
Django框架提供了ORM來解決數據持久化問題,ORM翻譯成中文叫“對象關系映射”。因為Python是面向對象的編程語言,我們在Python程序中使用對象模型來保存數據,而關系型數據庫使用關系模型,用二維表來保存數據,這兩種模型並不匹配。使用ORM是為了實現對象模型到關系模型的雙向轉換,這樣就不用在Python代碼中書寫SQL語句和遊標操作,因為這些都會由ORM自動完成。利用Django的ORM,我們可以直接將剛才創建的學科表和老師表變成Django中的模型類。
python manage.py inspectdb > polls/models.py我們可以對自動生成的模型類稍作調整,代碼如下所示。
from django.db import models class Subject(models.Model): no = models.AutoField(primary_key=True, verbose_name='編號') name = models.CharField(max_length=50, verbose_name='名稱') intro = models.CharField(max_length=1000, verbose_name='介紹') is_hot = models.BooleanField(verbose_name='是否熱門') class Meta: managed = False db_table = 'tb_subject' class Teacher(models.Model): no = models.AutoField(primary_key=True, verbose_name='編號') name = models.CharField(max_length=20, verbose_name='姓名') sex = models.BooleanField(default=True, verbose_name='性別') birth = models.DateField(verbose_name='出生日期') intro = models.CharField(max_length=1000, verbose_name='個人介紹') photo = models.ImageField(max_length=255, verbose_name='照片') good_count = models.IntegerField(default=0, db_column='gcount', verbose_name='好評數') bad_count = models.IntegerField(default=0, db_column='bcount', verbose_name='差評數') subject = models.ForeignKey(Subject, models.DO_NOTHING, db_column='sno') class Meta: managed = False db_table = 'tb_teacher'說明:模型類都直接或間接繼承自
Model類,模型類跟關系型數據庫的二維表對應,模型對象跟表中的記錄對應,模型對象的屬性跟表中的字段對應。如果對上面模型類的屬性定義不是特別理解,可以看看本文後面提供的“模型定義參考”部分的內容。
使用ORM完成模型的CRUD操作
有了Django框架的ORM,我們可以直接使用面向對象的方式來實現對數據的CRUD(增刪改查)操作。我們可以在PyCharm的終端中輸入下面的命令進入到Django項目的交互式環境,然後嘗試對模型的操作。
python manage.py shell
新增
from polls.models import Subject
subject1 = Subject(name='Python全棧開發', intro='當下最熱門的學科', is_hot=True)
subject1.save()
subject2 = Subject(name='全棧軟件測試', intro='學習自動化測試的學科', is_hot=False)
subject2.save()
subject3 = Subject(name='JavaEE分布式開發', intro='基於Java語言的服務器應用開發', is_hot=True)
刪除
subject = Subject.objects.get(no=2)
subject.delete()
更新
subject = Subject.objects.get(no=1)
subject.name = 'Python全棧+人工智能'
subject.save()
查詢
- 查詢所有對象。
Subjects.objects.all()
- 過濾數據。
# 查詢名稱為“Python全棧+人工智能”的學科
Subject.objects.filter(name='Python全棧+人工智能')
# 查詢名稱包含“全棧”的學科(模糊查詢)
Subject.objects.filter(name__contains='全棧')
Subject.objects.filter(name__startswith='全棧')
Subject.objects.filter(name__endswith='全棧')
# 查詢所有熱門學科
Subject.objects.filter(is_hot=True)
# 查詢編號大於3小於10的學科
Subject.objects.filter(no__gt=3).filter(no__lt=10)
Subject.objects.filter(no__gt=3, no__lt=10)
# 查詢編號在3到7之間的學科
Subject.objects.filter(no__ge=3, no__le=7)
Subject.objects.filter(no__range=(3, 7))
- 查詢單個對象。
# 查詢主鍵為1的學科
Subject.objects.get(pk=1)
Subject.objects.get(no=1)
Subject.objects.filter(no=1).first()
Subject.objects.filter(no=1).last()
- 排序。
# 查詢所有學科按編號升序排列
Subject.objects.order_by('no')
# 查詢所有部門按部門編號降序排列
Subject.objects.order_by('-no')
- 切片(分頁查詢)。
# 按編號從小到大查詢前3個學科
Subject.objects.order_by('no')[:3]
- 計數。
# 查詢一共有多少個學科
Subject.objects.count()
- 高級查詢。
# 查詢編號為1的學科的老師
Teacher.objects.filter(subject__no=1)
Subject.objects.get(pk=1).teacher_set.all()
# 查詢學科名稱有“全棧”二字的學科的老師
Teacher.objects.filter(subject__name__contains='全棧')
說明1:由於老師與學科之間存在多對一外鍵關聯,所以能通過學科反向查詢到該學科的老師(從一對多關系中“一”的一方查詢“多”的一方),反向查詢屬性默認的名字是
類名小寫_set(如上面例子中的teacher_set),當然也可以在創建模型時通過ForeingKey的related_name屬性指定反向查詢屬性的名字。如果不希望執行反向查詢可以將related_name屬性設置為'+'或者以'+'開頭的字符串。
說明2:ORM查詢多個對象時會返回QuerySet對象,QuerySet使用了惰性查詢,即在創建QuerySet對象的過程中不涉及任何數據庫活動,等真正用到對象時(對QuerySet求值)才向數據庫發送SQL語句並獲取對應的結果,這一點在實際開發中需要引起注意!
說明3:如果希望更新多條數據,不用先逐一獲取模型對象再修改對象屬性,可以直接使用QuerySet對象的
update()方法一次性更新多條數據。
利用Django後台管理模型
在創建好模型類之後,可以通過Django框架自帶的後台管理應用(admin應用)實現對模型的管理。雖然實際應用中,這個後台可能並不能滿足我們的需求,但是在學習Django框架時,我們可以利用admin應用來管理我們的模型,同時也通過它來了解一個項目的後台管理系統需要哪些功能。使用Django自帶的admin應用步驟如下所示。
-
將
admin應用所需的表遷移到數據庫中。admin應用本身也需要數據庫的支持,而且在admin應用中已經定義好了相關的數據模型類,我們只需要通過模型遷移操作就能自動在數據庫中生成所需的二維表。python manage.py migrate -
創建訪問
admin應用的超級用戶賬號,這里需要輸入用戶名、郵箱和口令。python manage.py createsuperuser說明:輸入口令時沒有回顯也不能退格,需要一氣呵成完成輸入。
-
運行項目,在瀏覽器中訪問
http://127.0.0.1:8000/admin,輸入剛才創建的超級用戶賬號和密碼進行登錄。
登錄後進入管理員操作平台。

注意,我們暫時還沒能在
admin應用中看到之前創建的模型類,為此需要在polls應用的admin.py文件中對需要管理的模型進行注冊。 -
注冊模型類。
from django.contrib import admin from polls.models import Subject, Teacher admin.site.register(Subject) admin.site.register(Teacher)注冊模型類後,就可以在後台管理系統中看到它們。

-
對模型進行CRUD操作。
可以在管理員平台對模型進行C(新增)、R(查看)、U(更新)、D(刪除)操作,如下圖所示。
-
添加學科。

-
查看所有學科。

-
刪除和更新學科。

-
-
注冊模型管理類。
可能大家已經注意到了,剛才在後台查看部門信息的時候,顯示的部門信息並不直觀,為此我們再修改
admin.py文件,通過注冊模型管理類,可以在後台管理系統中更好的管理模型。from django.contrib import admin from polls.models import Subject, Teacher class SubjectModelAdmin(admin.ModelAdmin): list_display = ('no', 'name', 'intro', 'is_hot') search_fields = ('name', ) ordering = ('no', ) class TeacherModelAdmin(admin.ModelAdmin): list_display = ('no', 'name', 'sex', 'birth', 'good_count', 'bad_count', 'subject') search_fields = ('name', ) ordering = ('no', ) admin.site.register(Subject, SubjectModelAdmin) admin.site.register(Teacher, TeacherModelAdmin)

為了更好的查看模型,我們為
Subject類添加__str__魔法方法,並在該方法中返回學科名字。這樣在如上圖所示的查看老師的頁面上顯示老師所屬學科時,就不再是Subject object(1)這樣晦澀的信息,而是學科的名稱。
補充內容
Django模型最佳實踐
- 正確的為模型和關系字段命名。
- 設置適當的
related_name屬性。 - 用
OneToOneField代替ForeignKeyField(unique=True)。 - 通過“遷移操作”(migrate)來添加模型。
- 用NoSQL來應對需要降低範式級別的場景。
- 如果布爾類型可以為空要使用
NullBooleanField。 - 在模型中放置業務邏輯。
- 用
<ModelName>.DoesNotExists取代ObjectDoesNotExists。 - 在數據庫中不要出現無效數據。
- 不要對
QuerySet調用len()函數。 - 將
QuerySet的exists()方法的返回值用於if條件。 - 用
DecimalField來存儲貨幣相關數據而不是FloatField。 - 定義
__str__方法。 - 不要將數據文件放在同一個目錄中。
說明:以上內容來自於STEELKIWI網站的Best Practice working with Django models in Python,有興趣的小夥伴可以閱讀原文。
模型定義參考
字段
對字段名稱的限制
- 字段名不能是Python的保留字,否則會導致語法錯誤
- 字段名不能有多個連續下劃線,否則影響ORM查詢操作
Django模型字段類
| 字段類 | 說明 |
|---|---|
AutoField |
自增ID字段 |
BigIntegerField |
64位有符號整數 |
BinaryField |
存儲二進制數據的字段,對應Python的bytes類型 |
BooleanField |
存儲True或False |
CharField |
長度較小的字符串 |
DateField |
存儲日期,有auto_now和auto_now_add屬性 |
DateTimeField |
存儲日期和日期,兩個附加屬性同上 |
DecimalField |
存儲固定精度小數,有max_digits(有效位數)和decimal_places(小數點後面)兩個必要的參數 |
DurationField |
存儲時間跨度 |
EmailField |
與CharField相同,可以用EmailValidator驗證 |
FileField |
文件上傳字段 |
FloatField |
存儲浮點數 |
ImageField |
其他同FileFiled,要驗證上傳的是不是有效圖像 |
IntegerField |
存儲32位有符號整數。 |
GenericIPAddressField |
存儲IPv4或IPv6地址 |
NullBooleanField |
存儲True、False或null值 |
PositiveIntegerField |
存儲無符號整數(只能存儲正數) |
SlugField |
存儲slug(簡短標注) |
SmallIntegerField |
存儲16位有符號整數 |
TextField |
存儲數據量較大的文本 |
TimeField |
存儲時間 |
URLField |
存儲URL的CharField |
UUIDField |
存儲全局唯一標識符 |
字段屬性
通用字段屬性
| 選項 | 說明 |
|---|---|
null |
數據庫中對應的字段是否允許為NULL,默認為False |
blank |
後台模型管理驗證數據時,是否允許為NULL,默認為False |
choices |
設定字段的選項,各元組中的第一個值是設置在模型上的值,第二值是人類可讀的值 |
db_column |
字段對應到數據庫表中的列名,未指定時直接使用字段的名稱 |
db_index |
設置為True時將在該字段創建索引 |
db_tablespace |
為有索引的字段設置使用的表空間,默認為DEFAULT_INDEX_TABLESPACE |
default |
字段的默認值 |
editable |
字段在後台模型管理或ModelForm中是否顯示,默認為True |
error_messages |
設定字段拋出異常時的默認消息的字典,其中的鍵包括null、blank、invalid、invalid_choice、unique和unique_for_date |
help_text |
表單小組件旁邊顯示的額外的幫助文本。 |
primary_key |
將字段指定為模型的主鍵,未指定時會自動添加AutoField用於主鍵,只讀。 |
unique |
設置為True時,表中字段的值必須是唯一的 |
verbose_name |
字段在後台模型管理顯示的名稱,未指定時使用字段的名稱 |
ForeignKey屬性
limit_choices_to:值是一個Q對象或返回一個Q對象,用於限制後台顯示哪些對象。related_name:用於獲取關聯對象的關聯管理器對象(反向查詢),如果不允許反向,該屬性應該被設置為'+',或者以'+'結尾。to_field:指定關聯的字段,默認關聯對象的主鍵字段。db_constraint:是否為外鍵創建約束,默認值為True。on_delete:外鍵關聯的對象被刪除時對應的動作,可取的值包括django.db.models中定義的:CASCADE:級聯刪除。PROTECT:拋出ProtectedError異常,阻止刪除引用的對象。SET_NULL:把外鍵設置為null,當null屬性被設置為True時才能這麽做。SET_DEFAULT:把外鍵設置為默認值,提供了默認值才能這麽做。
ManyToManyField屬性
symmetrical:是否建立對稱的多對多關系。through:指定維持多對多關系的中間表的Django模型。throughfields:定義了中間模型時可以指定建立多對多關系的字段。db_table:指定維持多對多關系的中間表的表名。
模型元數據選項
| 選項 | 說明 |
|---|---|
abstract |
設置為True時模型是抽象父類 |
app_label |
如果定義模型的應用不在INSTALLED_APPS中可以用該屬性指定 |
db_table |
模型使用的數據表名稱 |
db_tablespace |
模型使用的數據表空間 |
default_related_name |
關聯對象回指這個模型時默認使用的名稱,默認為 |
get_latest_by |
模型中可排序字段的名稱。 |
managed |
設置為True時,Django在遷移中創建數據表並在執行flush管理命令時把表移除 |
order_with_respect_to |
標記對象為可排序的 |
ordering |
對象的默認排序 |
permissions |
創建對象時寫入權限表的額外權限 |
default_permissions |
默認為('add', 'change', 'delete') |
unique_together |
設定組合在一起時必須獨一無二的字段名 |
index_together |
設定一起建立索引的多個字段名 |
verbose_name |
為對象設定人類可讀的名稱 |
verbose_name_plural |
設定對象的覆數名稱 |
查詢參考
按字段查找可以用的條件
exact/iexact:精確匹配/忽略大小寫的精確匹配查詢contains/icontains/startswith/istartswith/endswith/iendswith:基於like的模糊查詢in:集合運算gt/gte/lt/lte:大於/大於等於/小於/小於等於關系運算range:指定範圍查詢(SQL中的between…and…)year/month/day/week_day/hour/minute/second:查詢時間日期isnull:查詢空值(True)或非空值(False)search:基於全文索引的全文檢索(一般很少使用)regex/iregex:基於正則表達式的模糊匹配查詢
Comments