索引 1.定義模型類 2.模型類 3.欄位查詢 4.查詢集 5.模型類關係 6.模型類擴展 ORM簡介 ORM,全拼Object-Relation Mapping,中文意為對象-關係映射,是隨著面向對象的軟體開發方法發展而產生的。面向對象的開發方法是當今企業級應用開發環境中的主流開發方法,關係資料庫 ...
索引
ORM簡介
ORM,全拼Object-Relation Mapping,中文意為對象-關係映射,是隨著面向對象的軟體開發方法發展而產生的。面向對象的開發方法是當今企業級應用開發環境中的主流開發方法,關係資料庫是企業級應用環境中永久存放數據的主流數據存儲系統。對象和關係數據是業務實體的兩種表現形式,業務實體在記憶體中表現為對象,在資料庫中表現為關係數據。記憶體中的對象之間存在關聯和繼承關係,而在資料庫中,關係數據無法直接表達多對多關聯和繼承關係。因此,對象-關係映射ORM系統一般以中間件的形式存在,主要實現程式對象到關係資料庫數據的映射。面向對象是從軟體工程基本原則(如耦合、聚合、封裝)的基礎上發展起來的,而關係資料庫則是從數學理論發展而來的,兩套理論存在顯著的區別。為瞭解決這個不匹配的現象,對象關係映射技術應運而生。O/R中字母O起源於"對象"(Object),而R則來自於"關係"(Relational)。幾乎所有的程式裡面,都存在對象和關係資料庫。在業務邏輯層和用戶界面層中,我們是面向對象的。當對象信息發生變化的時候,我們需要把對象的信息保存在關係資料庫中。目前流行的ORM產品如Java的Hibernate,.Net的EntityFormerWork等。
在MVC框架中的Model模塊中都包括ORM,對於開發人員主要帶來瞭如下好處:
- 實現了數據模型與資料庫的解耦,通過簡單的配置就可以輕鬆更換資料庫,而不需要修改代碼。
- 只需要面向對象編程,不需要面向資料庫編寫代碼。
- 在MVC中Model中定義的類,通過ORM與關係型資料庫中的表對應,對象的屬性體現對象間的關係,這種關係也被映射到數據表中。
Django框架中ORM示意圖如下:
使用MySQL資料庫,這是Web項目首選的資料庫。
打開項目的settings.py文件,找到DATABASES項,預設使用SQLite3資料庫
修改為使用MySQL資料庫,代碼如下:
將引擎改為mysql,提供連接的主機HOST、埠PORT、資料庫名NAME、用戶名USER、密碼PASSWORD。
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'test2', #資料庫名字, 'USER': 'root', #資料庫登錄用戶名 'PASSWORD': '123456', #資料庫登錄密碼 'HOST': 'localhost', #資料庫所在主機 'PORT': '3306', #資料庫埠 } }
註意:資料庫test2 Django框架不會自動生成,需要我們自己進入mysql資料庫去創建。
把資料庫切換成了mysql,需要安裝pymysql模塊之後,Django框架才可以操作mysql資料庫。安裝命令如下:
pip install pymysql
安裝成功之後,在項目的_init_.py文件中加上如下代碼:
import pymysql pymysql.install_as_MySQLdb()
1.定義模型類
-
模型類被定義在"應用/models.py"文件中,此例中為"booktest/models.py"文件。
-
模型類必須繼承自Model類,位於包django.db.models中。
-
提示:對於重要數據使用邏輯刪除。
示例
1.打開booktest/models.py文件,定義模型類如下
from django.db import models #定義圖書模型類BookInfo class BookInfo(models.Model): btitle = models.CharField(max_length=20)#圖書名稱 bpub_date = models.DateField()#發佈日期 bread = models.IntegerField(default=0)#閱讀量 bcomment = models.IntegerField(default=0)#評論量 isDelete = models.BooleanField(default=False)#邏輯刪除 #定義英雄模型類HeroInfo class HeroInfo(models.Model): hname = models.CharField(max_length=20)#英雄姓名 hgender = models.BooleanField(default=True)#英雄性別 isDelete = models.BooleanField(default=False)#邏輯刪除 hcomment = models.CharField(max_length=200)#英雄描述信息 hbook = models.ForeignKey('BookInfo')#英雄與圖書表的關係為一對多,所以屬性定義在英雄模型類中
2.遷移
生成並執行遷移文件。
python manage.py makemigrations
python manage.py migrate
打開資料庫的命令行,查看當前所有表如下圖:
表bookinfo結構如圖:
預設值並不在資料庫層面生效,而是在django創建對象時生效。
表booktest_heroinfo結構如下:
Django框架會根據關係屬性生成一個關係欄位,並創建外鍵約束。
3.測試數據
在資料庫命令行中,複製如下語句執行,向booktest_bookinfo表中插入測試數據:
insert into booktest_bookinfo(btitle,bpub_date,bread,bcomment,isDelete) values ('射雕英雄傳','1980-5-1',12,34,0), ('天龍八部','1986-7-24',36,40,0), ('笑傲江湖','1995-12-24',20,80,0), ('雪山飛狐','1987-11-11',58,24,0);View Code
再複製如下語句執行,向booktest_heroinfo表中插入測試數據:
insert into booktest_heroinfo(hname,hgender,hbook_id,hcomment,isDelete) values ('郭靖',1,1,'降龍十八掌',0), ('黃蓉',0,1,'打狗棍法',0), ('黃藥師',1,1,'彈指神通',0), ('歐陽鋒',1,1,'蛤蟆功',0), ('梅超風',0,1,'九陰白骨爪',0), ('喬峰',1,2,'降龍十八掌',0), ('段譽',1,2,'六脈神劍',0), ('虛竹',1,2,'天山六陽掌',0), ('王語嫣',0,2,'神仙姐姐',0), ('令狐沖',1,3,'獨孤九劍',0), ('任盈盈',0,3,'彈琴',0), ('岳不群',1,3,'華山劍法',0), ('東方不敗',0,3,'葵花寶典',0), ('胡斐',1,4,'胡家刀法',0), ('苗若蘭',0,4,'黃衣',0), ('程靈素',0,4,'醫術',0), ('袁紫衣',0,4,'六合拳',0);View Code
4.定義視圖
打開booktest/views.py文件,定義視圖代碼如下:
from django.shortcuts import render,redirect from booktest.models import * from datetime import date from django.core.urlresolvers import reverse #查詢所有圖書並顯示 def index(request): book_list = BookInfo.objects.all() return render(request, 'booktest/index.html', {"book_list": book_list}) #創建新圖書 def create(request): book_obj = BookInfo() book_obj.btitle = "神雕俠侶" book_obj.bpub_date = date(1995, 12, 21) book_obj.save() # 跳轉首頁 return redirect(reverse('book:index')) #邏輯刪除指定編號的圖書 def delete(request,id): book_obj = BookInfo.objects.get(id=int(id)) book_obj.delete() return redirect(reverse('book:index'))View Code
5.配置url
打開項目的urls.py文件,配置url如下:
from django.conf.urls import include, url from django.contrib import admin urlpatterns = [ url(r'^admin/', admin.site.urls), #引入booktest的url配置 url(r'^book',include('booktest.urls')), ]View Code
在booktest應用下創建urls.py文件,代碼如下:
from django.conf.urls import url from booktest import views urlpatterns=[ url(r'^index$',views.index), url(r'^delete(\d+)/$',views.delete), url(r'^create/$',views.create), ]View Code
6.創建模板
打開項目的settings.py文件,配置模板查找目錄TEMPLATES的DIRS。
'DIRS': [os.path.join(BASE_DIR,'templates')],
在templates下創建index.html文件
index.html代碼如下:
<html> <head> <title>圖書管理</title> </head> <body> <a href="/create/">創建</a> <ul> {%for book in list%} <li>{{book.btitle}}--<a href="/delete{{book.id}}/">刪除</a></li> {%endfor%} </ul> </body> </html>
7.運行項目測試
2.模型類
定義屬性
Django根據屬性的類型確定以下信息:
-
當前選擇的資料庫支持欄位的類型
-
渲染管理表單時使用的預設html控制項
-
在管理站點最低限度的驗證
django會為表創建自動增長的主鍵列,每個模型只能有一個主鍵列,如果使用選項設置某屬性為主鍵列後django不會再創建自動增長的主鍵列。
預設創建的主鍵列屬性為id,可以使用pk代替,pk全拼為primary key。
註意:pk是主鍵的別名,若主鍵名為id2,那麼pk是id2的別名。
屬性命名限制:
- 不能是python的保留關鍵字。
- 不允許使用連續的下劃線,這是由django的查詢方式決定的。
- 定義屬性時需要指定欄位類型,通過欄位類型的參數指定選項,語法如下:
屬性=models.欄位類型(選項)
欄位類型
使用時需要引入django.db.models包,欄位類型如下:
-
AutoField:自動增長的IntegerField,通常不用指定,不指定時Django會自動創建屬性名為id的自動增長屬性。
-
BooleanField:布爾欄位,值為True或False。
-
NullBooleanField:支持Null、True、False三種值。
-
CharField(max_length=字元長度):字元串。
-
參數max_length表示最大字元個數。
-
-
TextField:大文本欄位,一般超過4000個字元時使用。
-
IntegerField:整數。
-
DecimalField(max_digits=None, decimal_places=None):十進位浮點數。
-
參數max_digits表示總位數。
-
參數decimal_places表示小數位數。
-
-
FloatField:浮點數。
-
DateField[auto_now=False, auto_now_add=False]):日期。
-
參數auto_now表示每次保存對象時,自動設置該欄位為當前時間,用於"最後一次修改"的時間戳,它總是使用當前日期,預設為false。
-
參數auto_now_add表示當對象第一次被創建時自動設置當前時間,用於創建的時間戳,它總是使用當前日期,預設為false。
-
參數auto_now_add和auto_now是相互排斥的,組合將會發生錯誤。
-
-
TimeField:時間,參數同DateField。
-
DateTimeField:日期時間,參數同DateField。
-
FileField:上傳文件欄位。
-
ImageField:繼承於FileField,對上傳的內容進行校驗,確保是有效的圖片。
- 。。。。。。
選項
通過選項實現對欄位的約束,選項如下:
-
null:如果為True,表示允許為空,預設值是False。
-
blank:如果為True,則該欄位允許為空白,預設值是False。
-
對比:null是資料庫範疇的概念,blank是表單驗證範疇的。
-
db_column:欄位的名稱,如果未指定,則使用屬性的名稱。
-
db_index:若值為True, 則在表中會為此欄位創建索引,預設值是False。
-
default:預設值。
-
primary_key:若為True,則該欄位會成為模型的主鍵欄位,預設值是False,一般作為AutoField的選項使用。
-
unique:如果為True, 這個欄位在表中必須有唯一值,預設值是False。
- 。。。。。。
綜合演示
修改booktest/models.py中的模型類,代碼如下:
from django.db import models #定義圖書模型類BookInfo class BookInfo(models.Model): #btitle = models.CharField(max_length=20)#圖書名稱 btitle = models.CharField(max_length=20, db_column='title')#通過db_column指定btitle對應表格中欄位的名字為title bpub_date = models.DateField()#發佈日期 bread = models.IntegerField(default=0)#閱讀量 bcomment = models.IntegerField(default=0)#評論量 isDelete = models.BooleanField(default=False)#邏輯刪除 #定義英雄模型類HeroInfo class HeroInfo(models.Model): hname = models.CharField(max_length=20)#英雄姓名 hgender = models.BooleanField(default=True)#英雄性別 isDelete = models.BooleanField(default=False)#邏輯刪除 #hcomment = models.CharField(max_length=200)#英雄描述信息 hcomment = models.CharField(max_length=200, null=True, blank=False) #hcomment對應的資料庫中的欄位可以為空,但通過後臺管理頁面添加英雄信息時hcomment對應的輸入框不能為空 hbook = models.ForeignKey('BookInfo')#英雄與圖書表的關係為一對多,所以屬性定義在英雄模型類中
然後生成遷移文件並執行遷移命令,最後查看資料庫中的內容。
3.欄位查詢
實現sql中where的功能,調用過濾器filter()、exclude()、get(),下麵以filter()為例。
通過"屬性名_id"表示外鍵對應對象的id值。
語法如下:
說明:屬性名稱和比較運算符間使用兩個下劃線,所以屬性名不能包括多個下劃線。
屬性名稱__比較運算符=值
打開booktest/views.py文件,在index視圖中編寫如下查詢代碼:
條件運算符
1) 查詢等
exact:表示判等。
例:查詢編號為1的圖書。
list=BookInfo.objects.filter(id__exact=1)
可簡寫為:
list=BookInfo.objects.filter(id=1)
2) 模糊查詢
contains:是否包含。
說明:如果要包含%無需轉義,直接寫即可。
例:查詢書名包含'傳'的圖書。
list = BookInfo.objects.filter(btitle__contains='傳')
startswith、endswith:以指定值開頭或結尾。
例:查詢書名以'部'結尾的圖書
list = BookInfo.objects.filter(btitle__endswith='部')
以上運算符都區分大小寫,在這些運算符前加上i表示不區分大小寫,如iexact、icontains、istartswith、iendswith.
3) 空查詢
isnull:是否為null。
例:查詢書名不為空的圖書。
list = BookInfo.objects.filter(btitle__isnull=False)
4) 範圍查詢
in:是否包含在範圍內。
例:查詢編號為1或3或5的圖書
list = BookInfo.objects.filter(id__in=[1, 3, 5])
5) 比較查詢
gt、gte、lt、lte:大於、大於等於、小於、小於等於。
例:查詢編號大於3的圖書
list = BookInfo.objects.filter(id__gt=3)
不等於的運算符,使用exclude()過濾器。
例:查詢編號不等於3的圖書
list = BookInfo.objects.exclude(id=3)
6) 日期查詢
year、month、day、week_day、hour、minute、second:對日期時間類型的屬性進行運算。
例:查詢1980年發表的圖書。
list = BookInfo.objects.filter(bpub_date__year=1980)
例:查詢1980年1月1日後發表的圖書。
list = BookInfo.objects.filter(bpub_date__gt=date(1990, 1, 1))
F對象
之前的查詢都是對象的屬性與常量值比較,兩個屬性怎麼比較呢? 答:使用F對象,被定義在django.db.models中。
語法如下:
F(屬性名)
例:查詢閱讀量大於等於評論量的圖書。
from django.db.models import F ... list = BookInfo.objects.filter(bread__gte=F('bcomment'))
可以在F對象上使用算數運算。
例:查詢閱讀量大於2倍評論量的圖書。
list = BookInfo.objects.filter(bread__gt=F('bcomment') * 2)
Q對象
多個過濾器逐個調用表示邏輯與關係,同sql語句中where部分的and關鍵字。
例:查詢閱讀量大於20,並且編號小於3的圖書。
list=BookInfo.objects.filter(bread__gt=20,id__lt=3)
或
list=BookInfo.objects.filter(bread__gt=20).filter(id__lt=3)
如果需要實現邏輯或or的查詢,需要使用Q()對象結合|運算符,Q對象被義在django.db.models中。
語法如下:
Q(屬性名__運算符=值)
例:查詢閱讀量大於20的圖書,改寫為Q對象如下。
from django.db.models import Q ... list = BookInfo.objects.filter(Q(bread__gt=20))
Q對象可以使用&、|連接,&表示邏輯與,|表示邏輯或。
例:查詢閱讀量大於20,或編號小於3的圖書,只能使用Q對象實現
list = BookInfo.objects.filter(Q(bread__gt=20) | Q(pk__lt=3))
Q對象前可以使用~操作符,表示非not。
例:查詢編號不等於3的圖書。
list = BookInfo.objects.filter(~Q(pk=3))
聚合函數
使用aggregate()過濾器調用聚合函數。聚合函數包括:Avg,Count,Max,Min,Sum,被定義在django.db.models中。
例:查詢圖書的總閱讀量。
from django.db.models import Sum ... list = BookInfo.objects.aggregate(Sum('bread'))
註意aggregate的返回值是一個字典類型,格式如下:
{'聚合類小寫__屬性名':值} 如:{'sum__bread':3}
使用count時一般不使用aggregate()過濾器。
例:查詢圖書總數。
list = BookInfo.objects.count()
註意count函數的返回值是一個數字。
分組查詢
假設現在有一張公司職員表:
我們使用原生SQL語句,按照部分分組求平均工資:
select dept,AVG(salary) from employee group by dept;
ORM查詢:
from django.db.models import Avg Employee.objects.values("dept").annotate(avg=Avg("salary").values(dept, "avg")
連表查詢的分組:
SQL查詢:
select dept.name,AVG(salary) from employee inner join dept on (employee.dept_id=dept.id) group by dept_id;
ORM查詢:
from django.db.models import Avg Dept.objects.annotate(avg=Avg("employee__salary")).values("name", "avg")
其他例子:
# 根據英雄的性別,求每個性別的英雄數量 hero_num = HeroInfo.objects.values("hgender").annotate(hero_count=Count("id"))
統計每一本書的作者個數 book_list = models.Book.objects.all().annotate(author_num=Count("author"))
統計出每個出版社買的最便宜的書的價格 publisher_list = models.Publisher.objects.annotate(min_price=Min("book__price"))
統計不止一個作者的圖書 Book.objects.annotate(author_num=Count("author")).filter(author_num__gt=1)
根據一本圖書作者數量的多少對查詢集 QuerySet進行排序 Book.objects.annotate(author_num=Count("author")).order_by("author_num")
查詢各個作者出的書的總價格 Author.objects.annotate(sum_price=Sum("book__price")).values("name", "sum_price")
4.查詢集
查詢集表示從資料庫中獲取的對象集合,在管理器上調用某些過濾器方法會返回查詢集,查詢集可以含有零個、一個或多個過濾器。過濾器基於所給的參數限制查詢的結果,從Sql的角度,查詢集和select語句等價,過濾器像where和limit子句。
返回查詢集的過濾器如下:
- all():返回所有數據。
- filter():返回滿足條件的數據。
- exclude():返回滿足條件之外的數據,相當於sql語句中where部分的not關鍵字。
- order_by():對結果進行排序。
返回單個值的過濾器如下:
- get():返回單個滿足條件的對象
-
如果未找到會引發"模型類.DoesNotExist"異常。
-
如果多條被返回,會引發"模型類.MultipleObjectsReturned"異常。
-
-
count():返回當前查詢結果的總條數。
- aggregate():聚合,返回一個字典。
判斷某一個查詢集中是否有數據:
- exists():判斷查詢集中是否有數據,如果有則返回True,沒有則返回False。
兩大特性
- 惰性執行:創建查詢集不會訪問資料庫,直到調用數據時,才會訪問資料庫,調用數據的情況包括迭代、序列化、與if合用。
- 緩存:使用同一個查詢集,第一次使用時會發生資料庫的查詢,然後把結果緩存下來,再次使用這個查詢集時會使用緩存的數據。
查詢集的緩存
每個查詢集都包含一個緩存來最小化對資料庫的訪問。
在新建的查詢集中,緩存為空,首次對查詢集求值時,會發生資料庫查詢,django會將查詢的結果存在查詢集的緩存中,並返回請求的結果,接下來對查詢集求值將重用緩存中的結果。
情況一:如下是兩個查詢集,無法重用緩存,每次查詢都會與資料庫進行一次交互,增加了資料庫的負載。
from booktest.models import BookInfo [book.id for book in BookInfo.objects.all()] [book.id for book in BookInfo.objects.all()]
情況二:經過存儲後,可以重用查詢集,第二次使用緩存中的數據。
list=BookInfo.objects.all() [book.id for book in list] [book.id for book in list]
限制查詢集
可以對查詢集進行取下標或切片操作,等同於sql中的limit和offset子句。
註意:不支持負數索引。
對查詢集進行切片後返回一個新的查詢集,不會立即執行查詢。
如果獲取一個對象,直接使用[0],等同於[0:1].get(),但是如果沒有數據,[0]引發IndexError異常,[0:1].get()如果沒有數據引發DoesNotExist異常。
5.模型類關係
關係欄位類型
關係型資料庫的關係包括三種類型:
-
ForeignKey:一對多,將欄位定義在多的一端中。
-
ManyToManyField:多對多,將欄位定義在任意一端中。
-
OneToOneField:一對一,將欄位定義在任意一端中。
-
可以維護遞歸的關聯關係,使用'self'指定,詳見"自關聯"。
一對多關係
參見booktest應用中的BookInfo類和HeroInfo類。
#定義圖書模型類BookInfo class BookInfo(models.Model): btitle = models.CharField(max_length=20)#圖書名稱 bpub_date = models.DateField()#發佈日期 bread = models.IntegerField(default=0)#閱讀量 bcomment = models.IntegerField(default=0)#評論量 isDelete = models.BooleanField(default=False)#邏輯刪除 #定義英雄模型類HeroInfo class HeroInfo(models.Model): hname = models.CharField(max_length=20)#英雄姓名 hgender = models.BooleanField(default=True)#英雄性別 isDelete = models.BooleanField(default=False)#邏輯刪除 hcomment = models.CharField(max_length=200)#英雄描述信息 hbook = models.ForeignKey('BookInfo')#英雄與圖書表的關係為一對多,所以屬性定義在英雄模型類中
多對多關係
我們下麵設計一個新聞類和新聞類型類,一個新聞類型下可以用很多條新聞,一條新聞也可能歸屬於多種新聞類型。
重新在項目下新建一個應用newstest,註冊應用之後,編輯newstest/models.py文件設計模型類。
class TypeInfo(models.Model): tname = models.CharField(max_length=20) #新聞類別 class NewsInfo(models.Model): ntitle = models.CharField(max_length=60) #新聞標題 ncontent = models.TextField() #新聞內容 npub_date = models.DateTimeField(auto_now_add=True) #新聞發佈時間 ntype = models.ManyToManyField('TypeInfo') #通過ManyToManyField建立TypeInfo類和NewsInfo類之間多對多的關係
關聯查詢
Django中也能實現類似於join查詢。
通過對象執行關聯查詢
在定義模型類時,可以指定三種關聯關係,最常用的是一對多關係,如本例中的"圖書-英雄"就為一對多關係。
由一到多的訪問語法:
一對應的模型類對象.多對應的模型類名小寫_set 例: b = Bo