西安最好的網(wǎng)站建設(shè)公司品牌推廣思路
????????上一節(jié)提到了Django是基于MVC架構(gòu)的Web框架,MVC架構(gòu)追求的是“模型”和“視圖”的解耦合。所謂“模型”說得更直白一些就是數(shù)據(jù)(的表示),所以通常也被稱作“數(shù)據(jù)模型”。在實際的項目中,數(shù)據(jù)模型通常通過數(shù)據(jù)庫實現(xiàn)持久化操作,而關(guān)系型數(shù)據(jù)庫在過去和當(dāng)下都是持久化的首選方案,下面我們通過完成一個投票項目來講解和模型相關(guān)的知識點。投票項目的首頁會展示某在線教育平臺所有的學(xué)科;點擊學(xué)科可以查看到該學(xué)科的老師及其信息;用戶登錄后在查看老師的頁面為老師投票,可以投贊成票和反對票;未登錄的用戶可以通過登錄頁進行登錄;尚未注冊的用戶可以通過注冊頁輸入個人信息進行注冊。在這個項目中,我們使用MySQL數(shù)據(jù)庫來實現(xiàn)數(shù)據(jù)持久化操作。
1 ORM模型
1.1 ORM介紹
對象關(guān)系映射(Object Relational Mapping,簡稱ORM)模式是一種為了解決面向?qū)ο笈c關(guān)系數(shù)據(jù)庫存在的互不匹配的現(xiàn)象的技術(shù)。
????????簡單的說,ORM 是通過使用描述對象和數(shù)據(jù)庫之間映射的元數(shù)據(jù),將程序中的對象自動持久化到關(guān)系數(shù)據(jù)庫中。ORM 在業(yè)務(wù)邏輯層和數(shù)據(jù)庫層之間充當(dāng)了橋梁的作用。ORM 解決的主要問題是對象和關(guān)系的映射。它通常把一個類和一個表一一對應(yīng),類的每個實例對應(yīng)表中的一條記錄,類的每個屬性對應(yīng)表中的每個字段,具體如下圖所示。ORM 提供了對數(shù)據(jù)庫的映射,不用直接編寫 SQL 代碼,只需像操作對象一樣從數(shù)據(jù)庫操作數(shù)據(jù)。讓軟件開發(fā)人員專注于業(yè)務(wù)邏輯的處理,提高了開發(fā)效率。

????????ORM 模式也是有一定缺點的,它會在一定程度上犧牲程序的執(zhí)行效率。此外,還存在許多復(fù)雜場景是 ORM 模式無法解決的,同樣還是需要手動編寫 SQL 語句完成。
1.2 創(chuàng)建 Django 項目
????????首先創(chuàng)建Django項目vote,在項目下創(chuàng)建名為polls的應(yīng)用和保存模板頁的文件夾tempaltes,項目文件夾的結(jié)構(gòu)如下所示。


????????根據(jù)上面描述的項目需求,這里準備了四個靜態(tài)頁面,分別是展示學(xué)科的頁面subjects.html
,顯示學(xué)科老師的頁面teachers.html
,登錄頁面login.html
,注冊頁面register.html
,稍后我們會將靜態(tài)頁修改為Django項目所需的模板頁。
1.3 數(shù)據(jù)庫中生成模型表
????????在 Django 中,一個模型(model)會映射到一個數(shù)據(jù)庫表。每個模型都是一個 Python 類,它是django.db.models.Model
的子類,模型的每個屬性都代表一個數(shù)據(jù)庫字段。
(1) 在 polls 中添加數(shù)據(jù)模型
????????在 polls 的 models.py
中添加如下代碼:
from django.db import models # 引入Django.db.models模塊class Subject(models.Model):"""編寫Subject模型類,數(shù)據(jù)模型應(yīng)該繼承于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='是否熱門')def __str__(self):return self.nameclass Meta:# 通過db_table自定義數(shù)據(jù)表名db_table = 'tb_subject'class Teacher(models.Model):"""編寫Teacher模型類,數(shù)據(jù)模型應(yīng)該繼承于models.Model或其子類"""sex_choices = ((0, '女'),(1, '男'),)no = models.AutoField(primary_key=True, verbose_name='編號')name = models.CharField(max_length=20, verbose_name='姓名')sex = models.BooleanField(default=True, verbose_name='性別', choices=sex_choices)birth = models.DateField(verbose_name='出生日期')intro = models.CharField(max_length=1000, verbose_name='個人介紹')photo = models.ImageField(max_length=255, verbose_name='照片')gcount = models.IntegerField(default=0, db_column='gcount', verbose_name='好評數(shù)')bcount = models.IntegerField(default=0, db_column='bcount', verbose_name='差評數(shù)')sno = models.ForeignKey(Subject, on_delete=models.CASCADE, db_column='sno')def __str__(self):return self.nameclass Meta:db_table = 'tb_teacher'
????????Subject 和 Teacher 模型中的每一個屬性都指明了models下面的一個數(shù)據(jù)類型,代表了數(shù)據(jù)庫中的一個字段。上面的類在數(shù)據(jù)庫中會創(chuàng)建如下的表,見1.3節(jié)的第二步創(chuàng)建表格的過程。
(2) 遷移模型
????????使用 Django 給我們提供的兩個命令來在數(shù)據(jù)庫中生成 polls 應(yīng)用下定義的數(shù)據(jù)模型,第一步生成遷移文件,第二步將遷移文件應(yīng)用到數(shù)據(jù)庫:
python manage.py makemigrations
python manage.py migrate
1.3 自動生成數(shù)據(jù)模型
1. 配置關(guān)系型數(shù)據(jù)庫MySQL
(1) 在MySQL中創(chuàng)建數(shù)據(jù)庫,創(chuàng)建用戶,授權(quán)用戶訪問該數(shù)據(jù)庫。
create database vote default charset utf8;
create user 'username'@'%' identified by 'yourpassword';
grant all privileges on vote.* to 'username'@'%';
flush privileges;
(2) 在 MySQL 中創(chuàng)建保存學(xué)科和老師信息的二維表(保存用戶信息的表稍后處理)。
use vote;-- 創(chuàng)建學(xué)科表
create table `tb_subject`
(`no` integer auto_increment comment '學(xué)科編號',`name` varchar(50) not null comment '學(xué)科名稱',`intro` varchar(1000) not null default '' comment '學(xué)科介紹',`is_hot` boolean not null default 0 comment '是不是熱門學(xué)科',primary key (`no`)
);
-- 創(chuàng)建老師表
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 '好評數(shù)',`bcount` integer not null default 0 comment '差評數(shù)',`sno` integer not null comment '所屬學(xué)科',primary key (`no`),foreign key (`sno`) references `tb_subject` (`no`)
);
(3) 安裝數(shù)據(jù)庫的驅(qū)動,Python 3.x 使用 pymysql 作為 MySQL的驅(qū)動,然后在Django項目文件夾的__init__.py
中添加如下所示的代碼:
import pymysql
pymysql.install_as_MySQLdb() # 為了pymysql發(fā)揮最大數(shù)據(jù)庫操作性能
???????? 溫馨提示: 如果使用Django 2.2及以上版本,還會遇到PyMySQL跟Django框架的兼容性問題,兼容性問題會導(dǎo)致項目無法運行,需要按照GitHub上PyMySQL倉庫Issues中提供的方法進行處理??傮w來說,使用pymysql會比較麻煩,強烈建議大家首選安裝mysqlclient,mysqlclient執(zhí)行效率也比較高。
(4) 修改項目的settings.py文件,首先將我們創(chuàng)建的應(yīng)用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', # 把應(yīng)用文件加入
]ATABASES = {'default': {# 數(shù)據(jù)庫引擎配置'ENGINE': 'django.db.backends.mysql',# 數(shù)據(jù)庫的名字'NAME': 'vote',# 數(shù)據(jù)庫服務(wù)器的IP地址(本機可以寫為localhost或127.0.0.1)'HOST': 'localhost',# 啟動MySQL服務(wù)的端口號'PORT': 3306,# 數(shù)據(jù)庫用戶名和口令'USER': 'carpediem','PASSWORD': 'carpediem2021',# 數(shù)據(jù)庫使用的字符集'CHARSET': 'utf8',# 數(shù)據(jù)庫時間日期的時區(qū)設(shè)定'TIME_ZONE': 'Asia/Chongqing',}
}
????????在配置ENGINE屬性時,常用的可選值包括:
'django.db.backends.sqlite3'
:SQLite嵌入式數(shù)據(jù)庫。'django.db.backends.postgresql'
:BSD許可證下發(fā)行的開源關(guān)系型數(shù)據(jù)庫產(chǎn)品。'django.db.backends.mysql'
:甲骨文公司經(jīng)濟高效的數(shù)據(jù)庫產(chǎn)品。'django.db.backends.oracle'
:甲骨文公司關(guān)系型數(shù)據(jù)庫旗艦產(chǎn)品。
????????其他的配置可以參考官方文檔中的數(shù)據(jù)庫配置部分。
(5) Django框架提供了ORM來解決數(shù)據(jù)持久化問題,ORM翻譯成中文叫“對象關(guān)系映射”。因為Python是面向?qū)ο蟮木幊陶Z言,我們在Python程序中使用對象模型來保存數(shù)據(jù),而關(guān)系型數(shù)據(jù)庫使用關(guān)系模型,用二維表來保存數(shù)據(jù),這兩種模型并不匹配。使用ORM是為了實現(xiàn)對象模型到關(guān)系模型的雙向轉(zhuǎn)換,這樣就不用在Python代碼中書寫SQL語句和游標(biāo)操作,因為這些都會由ORM自動完成。利用Django的ORM,我們可以直接將剛才創(chuàng)建的學(xué)科表和老師表變成Django中的模型類。
python manage.py inspectdb > polls/models.py
我們可以對自動生成的模型類稍作調(diào)整,代碼如下所示。
from django.db import modelsclass 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 = Falsedb_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='照片')gcount = models.IntegerField(default=0, db_column='gcount', verbose_name='好評數(shù)')bcount = models.IntegerField(default=0, db_column='bcount', verbose_name='差評數(shù)')sno = models.ForeignKey(Subject, models.DO_NOTHING, db_column='sno')class Meta:managed = Falsedb_table = 'tb_teacher'
????????若你的確想要允許 Django 管理這些表格的生命周期,你需要將上面的 managed 選項的值改為 True (或者刪掉它,因為 True 是默認值)
???????? 溫馨提示: 所有模型都是django.db.models.Model
類的子類,模型類跟關(guān)系型數(shù)據(jù)庫的二維表對應(yīng),模型對象跟表中的記錄對應(yīng),模型對象的屬性跟表中的字段對應(yīng)。每個字段由django.db.models.Field
子類(內(nèi)置在Django core)的實例表示,它們并將被轉(zhuǎn)換為數(shù)據(jù)庫的列。
????????該功能僅是一個快捷方式,不是最佳的創(chuàng)建模型的方法。參考 inspectdb 文檔 獲取更多信息
補充:
1. 通用字段屬性
選項 | 說明 |
---|---|
null | 數(shù)據(jù)庫中對應(yīng)的字段是否允許為NULL ,默認為False |
blank | 后臺模型管理驗證數(shù)據(jù)時,是否允許為NULL ,默認為False |
choices | 設(shè)定字段的選項,各元組中的第一個值是設(shè)置在模型上的值,第二值是人類可讀的值 |
db_column | 字段對應(yīng)到數(shù)據(jù)庫表中的列名,未指定時直接使用字段的名稱 |
db_index | 設(shè)置為True 時將在該字段創(chuàng)建索引 |
db_tablespace | 為有索引的字段設(shè)置使用的表空間,默認為DEFAULT_INDEX_TABLESPACE |
default | 字段的默認值 |
editable | 字段在后臺模型管理或ModelForm 中是否顯示,默認為True |
error_messages | 設(shè)定字段拋出異常時的默認消息的字典,其中的鍵包括null 、blank 、invalid 、invalid_choice 、unique 和unique_for_date |
help_text | 表單小組件旁邊顯示的額外的幫助文本。 |
primary_key | 將字段指定為模型的主鍵,未指定時會自動添加AutoField 用于主鍵,只讀。 |
unique | 設(shè)置為True 時,表中字段的值必須是唯一的 |
verbose_name | 字段在后臺模型管理顯示的名稱,未指定時使用字段的名稱 |
2. ForeignKey
屬性
limit_choices_to
:值是一個Q對象或返回一個Q對象,用于限制后臺顯示哪些對象。related_name
:用于獲取關(guān)聯(lián)對象的關(guān)聯(lián)管理器對象(反向查詢),如果不允許反向,該屬性應(yīng)該被設(shè)置為'+'
,或者以'+'
結(jié)尾。to_field
:指定關(guān)聯(lián)的字段,默認關(guān)聯(lián)對象的主鍵字段。db_constraint
:是否為外鍵創(chuàng)建約束,默認值為True
。on_delete
:外鍵關(guān)聯(lián)的對象被刪除時對應(yīng)的動作,可取的值包括django.db.models
中定義的:CASCADE
:級聯(lián)刪除。PROTECT
:拋出ProtectedError
異常,阻止刪除引用的對象。SET_NULL
:把外鍵設(shè)置為null
,當(dāng)null
屬性被設(shè)置為True
時才能這么做。SET_DEFAULT
:把外鍵設(shè)置為默認值,提供了默認值才能這么做。
3. ManyToManyField
屬性
symmetrical
:是否建立對稱的多對多關(guān)系。through
:指定維持多對多關(guān)系的中間表的Django模型。throughfields
:定義了中間模型時可以指定建立多對多關(guān)系的字段。db_table
:指定維持多對多關(guān)系的中間表的表名。
4. 模型元數(shù)據(jù)選項
選項 | 說明 |
---|---|
abstract | 設(shè)置為True時模型是抽象父類 |
app_label | 如果定義模型的應(yīng)用不在INSTALLED_APPS中可以用該屬性指定 |
db_table | 模型使用的數(shù)據(jù)表名稱 |
db_tablespace | 模型使用的數(shù)據(jù)表空間 |
default_related_name | 關(guān)聯(lián)對象回指這個模型時默認使用的名稱,默認為<model_name>_set |
get_latest_by | 模型中可排序字段的名稱。 |
managed | 設(shè)置為True時,Django在遷移中創(chuàng)建數(shù)據(jù)表并在執(zhí)行flush管理命令時把表移除 |
order_with_respect_to | 標(biāo)記對象為可排序的 |
ordering | 對象的默認排序 |
permissions | 創(chuàng)建對象時寫入權(quán)限表的額外權(quán)限 |
default_permissions | 默認為('add', 'change', 'delete') |
unique_together | 設(shè)定組合在一起時必須獨一無二的字段名 |
index_together | 設(shè)定一起建立索引的多個字段名 |
verbose_name | 為對象設(shè)定人類可讀的名稱 |
verbose_name_plural | 設(shè)定對象的復(fù)數(shù)名稱 |
2 使用ORM完成模型的CRUD操作
2.1 基本的增刪改查
????????有了Django框架的ORM,我們可以直接使用面向?qū)ο蟮姆绞絹韺崿F(xiàn)對數(shù)據(jù)的CRUD(增刪改查)操作。我們可以在PyCharm的終端中輸入下面的命令進入到Django項目的交互式環(huán)境,然后嘗試對模型的操作。
python manage.py shell
1. 新增
from polls.models import SubjectSubject.objects.create(name='H5前端開發(fā)', intro='前段比較熱的學(xué)科', is_hot=True)subject1 = Subject(name='Python全棧開發(fā)', intro='當(dāng)下最熱門的學(xué)科', is_hot=True)
subject1.save()
subject2 = Subject(name='全棧軟件測試', intro='學(xué)習(xí)自動化測試的學(xué)科', is_hot=False)
subject2.save()
subject3 = Subject(name='JavaEE分布式開發(fā)', intro='基于Java語言的服務(wù)器應(yīng)用開發(fā)', is_hot=True)
2. 刪除
subject = Subject.objects.get(no=2)
subject.delete()
3. 更新
subject = Subject.objects.get(no=1)
subject.name = 'Python全棧+人工智能'
subject.save()
4. 查詢
(1) 查詢所有對象
Subject.objects.all()
(2) 過濾數(shù)據(jù)
# 查詢名稱為“Python全棧+人工智能”的學(xué)科
Subject.objects.filter(name='Python全棧+人工智能')# 查詢名稱包含“全?!钡膶W(xué)科(模糊查詢)
Subject.objects.filter(name__contains='全棧')
Subject.objects.filter(name__startswith='全棧')
Subject.objects.filter(name__endswith='全棧')# 查詢所有熱門學(xué)科
Subject.objects.filter(is_hot=True)# 查詢編號大于3小于10的學(xué)科
Subject.objects.filter(no__gt=3).filter(no__lt=10)
Subject.objects.filter(no__gt=3, no__lt=10)# 查詢編號在3到7之間的學(xué)科
Subject.objects.filter(no__gte=3, no__lte=7)
Subject.objects.filter(no__range=(3, 7))
(3) 查詢單個對象
# 查詢主鍵為1的學(xué)科
Subject.objects.get(pk=1)
Subject.objects.get(no=1)
Subject.objects.filter(no=1).first()
Subject.objects.filter(no=1).last()
(4) 排序
# 查詢所有學(xué)科按編號升序排列
Subject.objects.order_by('no')
# 查詢所有部門按部門編號降序排列
Subject.objects.order_by('-no')
(5) 切片
# 按編號從小到大查詢前3個學(xué)科
Subject.objects.order_by('no')[:3]
(6) 計數(shù)
# 查詢一共有多少個學(xué)科
Subject.objects.count()
(7) 高級查詢
# 查詢編號為1的學(xué)科的老師
Teacher.objects.filter(sno__no=1)
Subject.objects.get(pk=1).teacher_set.all() # 查詢學(xué)科名稱有“全?!倍值膶W(xué)科的老師
Teacher.objects.filter(sno__name__contains='全棧')
????????上面的 objects 是一個特殊的屬性,通過它來查詢數(shù)據(jù)庫,它是模型的一個 Manager。在 filter() 方法中還有一些比較神奇的雙下劃線輔助我們進一步過濾結(jié)果,詳細了解,請閱讀官方文檔————執(zhí)行查詢
溫馨提示:
- 說明1:由于老師與學(xué)科之間存在多對一外鍵關(guān)聯(lián),所以能通過學(xué)科反向查詢到該學(xué)科的老師(從一對多關(guān)系中“一”的一方查詢“多”的一方),反向查詢屬性默認的名字是類名小寫_set(如上面例子中的teacher_set),當(dāng)然也可以在創(chuàng)建模型時通過ForeingKey的related_name屬性指定反向查詢屬性的名字。如果不希望執(zhí)行反向查詢可以將related_name屬性設(shè)置為’+‘或者以’+'開頭的字符串。
- 說明2:ORM查詢多個對象時會返回QuerySet對象,QuerySet使用了惰性查詢,即在創(chuàng)建QuerySet對象的過程中不涉及任何數(shù)據(jù)庫活動,等真正用到對象時(對QuerySet求值)才向數(shù)據(jù)庫發(fā)送SQL語句并獲取對應(yīng)的結(jié)果,這一點在實際開發(fā)中需要引起注意!
- 說明3:如果希望更新多條數(shù)據(jù),不用先逐一獲取模型對象再修改對象屬性,可以直接使用QuerySet對象的
update()
方法一次性更新多條數(shù)據(jù)。
參考
- Django 使用原生的 SQL 語句操作 MySQL 數(shù)據(jù)庫:https://www.imooc.com/wiki/djangolesson/nativesql.html
- 深入模型:https://gitee.com/zengyujin/Python-100-Days/blob/master/Day41-55/42.深入模型.md
- Django入門指南-第5章:模型設(shè)計:https://www.bookstack.cn/read/django-beginners-guide-zh/Fundamentals-2.md
- Django v4.0 中文文檔 模型:https://www.bookstack.cn/read/Django-4.0-zh/94d959954f3d0daa.md