Django中的ORM
1. 資料庫的配置
Django可以配置使用sqlite3,mysql,oracle,postgresql等資料庫
在一個Django專案中,默認使用的是sqlite3資料庫
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',#默認使用的資料庫引擎是sqlite3,專案自動創建
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),#指定資料庫所在的路徑
}
}
如果想在一個Django專案中配置使用mysql資料庫,可以使用如下配置:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',#表示使用的是mysql資料庫的引擎
'NAME': 'db1', #資料庫的名字,可以在mysql的提示符下先創建好
'USER':'root', #資料庫用戶名
'PASSWORD':'', #資料庫密碼
'HOST':'', #資料庫主機,留空默認為"localhost"
'PORT':'3306', #資料庫使用的埠
}
}
配置好資料庫的資訊后還必須安裝資料庫的驅動程式
Django默認匯入的mysql的驅動程式是MySQLdb,然而MySQLdb對于py3支持不全,所以這里使用PyMySQL
在專案名檔案下的__init__.py檔案中寫入如下配置:
import pymysql
pymysql.install_as_MySQLdb()
2. ORM表模型
人的模型:每個人都有只屬性自己的身份資訊,包含生日,性別,身份證號等,每個人和他的資訊是一對一的關系,
因此可以把一個人的所有身份資訊匯總到一張資料表中,沒必要拆分成兩張表,這是一對一(one-to-one)的概念
書的模型:一本書有書名,出版日期,價格,所屬的出版社等資訊.一本書可以有多個作者來撰寫,一個作者也可以寫作多本書,
書與作者是多對多的關聯關系,這是多對多(many-to-many)的概念
同時,一本書只能由一個出版社出版,但是一個出版社可以出版多本書,所以出版社與書是一對多的關系,這是一對多(one-to-many)的概念
每個資料模型都是Django.db.models.Model的子類,它的父類Model包含了所有必要的和資料庫互動的方法,并提供了一個簡單的定義資料庫欄位的語法
每個模型相當于單個資料庫表(多對多關系例外,會多生成一張關系表),每個屬性都是資料表中的欄位.
屬性名就是欄位名,其型別(例如CharField)相當于資料庫的欄位型別(例如varchar).
因此在Django的資料庫中:
表名對應為python中的類名
欄位對應python中的類屬性
表中的每一條記錄對應python中的類實體物件
資料模型的三種關系:
一對一模型:實質就是在主外鍵(foreign key)的關系基礎上,給外鍵加了一個unique=True的屬性
一對多模型:就是主外鍵關系(foreign key)
多對多模型:(ManyToManyField)自動創建第三張表,也可以手動創建第三張表:兩個foreign key
例子,創建一個包含一對一,一對多和多對多關系的資料表:
#創建一個書的類,繼承models類
class Book(models.Model):
#用models類創建書的名字,型別為字串,CharField相當于mysql陳述句中的varchar,欄位最長為32
title = models.CharField(max_length=32)
#創建書的價格,型別為浮點型,小數點前最長4位,小數點后最長2位
price = models.DecimalField(max_digits=6, decimal_places=2)
#創建書的出版社資訊,其與出版社的外鍵關系為一對多,所以用外鍵
publish = models.ForeignKey(Publish)
#創建書的出版日期,型別為日期
publication_date = models.DateField()
#創建書的型別資訊,為字串型別,最長為20
classification=models.CharField(max_length=20)
#創建書的作者資訊,書籍與作者的關系為多對多,所以使用many-to-many
authors = models.ManyToManyField("Author")
3. ORM之增(create,save)
例如,為資料庫中插入書的資訊,有兩種方式
3.1 使用create方式
方式一:
Publish.objects.create("name"="人民出版社",city="北京"}
方式二:
Publish.objects.create(**{"name":"文藝出版社","city":"上海"}}
3.2 使用save方式
方式一:
book1=Book(title="python",price="88",publish_id="1",publication_date="2017-06-18")
book1.save()
方式二:
author1=Author(name="jerry")
author1.save()
上面創建的都是一對一的資訊
3.3 一對多的資訊的創建(Foreignkey)
方式一:
#獲取出版社物件
publish_obj=Publish.objects.get(id=4)
#將出版社的物件系結到書籍的記錄中
Book.objects.create(
title="python",
price=48.00,
publication_date="2017-07-12",
publish=publish_obj,
)
方式二:
#直接把出版社的id號插入到書籍的記錄中
Book.objects.create(
title="python",
price=48.00,
publish_id=2,
publication_date="2017-06-18",
)
3.4 多對多資訊的創建(ManyToManyField())
3.4.1 為一本書添加多個作者
author1=Author.objects.get(id=1)#獲取id號為1的作者物件
author2=Author.objects.filter(name="tom")#獲取名字為"tom"的作者物件
book1=Book.objects.get(id=2)#獲取id號為2的書籍物件
book1.authors.add(author1,author2)#為書籍物件添加多個作者物件
也可以用這種方式:
book1.authors.add(*[author1,author2])#為書籍物件添加作者物件的串列
book1.authors.remove(*[author1,author2])#洗掉指定書籍的所有作者
3.4.2 為一個作者添加多本書
author_obj = Author.objects.filter(name="jerry")#獲取名字為"jerry"的作者物件
book_obj=Book.objects.filter(id__gt=3)#獲取id大于3的書籍物件集合
author_obj.book_set.add(*book_obj)#為作者物件添加書籍物件集合
author_obj.book_set.remove(*book_obj)#洗掉指定作者物件所有的書籍
使用models.ManyToManyField()會自動創建第三張表
3.5 手動創建多對多的作者與書籍資訊表
class Book2Author(models.Models):
author=models.ForeignKey("Author")#為作者指定Author這張表做為外鍵
book=models.ForeignKey("Book")#為書籍指定Book這張表做為外鍵
author_obj=models.Author.objects.filter(id=3)[0]#獲取Author表中id為3的作者物件
book_obj=models.Book.objects.filter(id=4)[0]#獲取Book表中id為4的書籍物件
3.5.1 方式一:
obj1=Book2Author.objects.create(author=author_obj,book=book_obj)
obj1.save()
3.5.2 方式二:
obj2=Book2Author(author=author_obj,book=book_obj)
obj2.save()
4. ORM之刪(delete)
陳述句格式:
Book.objects.filter(id=1).delete()
這個操作不僅會洗掉Book表中的一條記錄,同時也會洗掉書籍與作者表中與Book相關聯的記錄,這種洗掉方式是Django默認的級聯洗掉
5. ORM之查(filter,value)
5.1 方法
filter(**kwargs) # 包含了與所給篩選條件相匹配的物件
all() # 查詢所有結果
get(**kwargs) # 回傳與所給篩選條件相匹配的物件,回傳結果有且只有一個,如果符合篩選條件的物件超過一個或者沒有都是報錯
values(*field) # 回傳一個ValueQuerySet,運行后得到的并不是一系列model的實體化物件,而是一個可迭代的字典序列
exclude(**kwargs) # 包含了與所給的篩選條件不匹配的物件
order by(*field) # 對查詢結果排序
reverse() # 對查詢結果反向排序
distinct() # 從回傳結果中剔除重復記錄
values_list(*field) # 與values()非常相似,回傳一個元組序列,values回傳一個字典序列
count() # 回傳資料庫中匹配的記錄的數量
first() # 回傳資料庫中匹配的物件的第一個物件
last() # 回傳資料庫中匹配的物件的最后一個物件
exists() # 判斷一個物件集合中是否包含指定物件,包含回傳True,不包含回傳False
5.2 QuerySet與惰性機制
所謂惰性機制,Publisher.objects.all()或者.filter()等都只是回傳一個QuerySet(查詢結果集合物件)
其不會立即執行sql查詢,而是當呼叫QuerySet的時候才會執行sq陳述句
5.2.1 QuerySet的特點
可迭代的
可切片
例子:
obj=Book.objects.all()#得到一個物件集合
for item in obj:#對QuerySet進行迭代,每一個item就是一個行物件
print("item":,item)
#對QuerySet進行切片
print(obj[1])
print(obj[1:4])
print(obj[::-1])
5.2.2 Django的QuerySet是惰性的
Django的QuerySet對應于資料庫的記錄,通過設定的條件進行過濾.
一個簡單的查詢并不會運行任何的資料庫查詢.只有遍歷QuerySet或者使用if陳述句的時候,才會執行sql陳述句
例子:
book_obj=Book.objects.filter(id=3)
for i in book_ojb:#到這一步才會真正執行sql查詢
print(i)
if book_obj:#到這一步才會真正執行sql查詢
print("ok")
5.2.3 QuerySet是具有cache的
當遍歷QuerySet時,會從資料庫中獲取匹配的記錄,然后轉換成Django的model,此時執行sql陳述句
這些model會保存在QuerySet內置的cache中,這樣如果你再次遍歷整個QuerySet,就不會再次執行sql查詢
例子:
book_obj=Book.objects.filter(id=3)
for i in book_obj:
print(i)
for i in book_obj:#執行兩次遍歷,結果只會列印一次結果
print(i)
5.2.4 簡單的使用if陳述句進行判斷也會完全執行整個QuerySet并且把資料放入cache,可以使用exists()方法來判斷是否有資料
例子:
book_obj=Book.objects.filter(id=4)#獲取Book表中id為4的物件
if book_obj.exists():#exists()的檢查可以避免資料放入QuerySet的cache中
print("hello world")
5.2.5 當處理的記錄數量很大時,cache會占用很多記憶體
巨大的QuerySet可能會鎖住系統行程,使用程式崩潰.避免在遍歷資料的同時產生QuerySet的cache,可以使用iterator()方法來獲取資料,等到資料迭代并處理完就會被丟棄
例子:
book_obj=Book.objects.all().iterator()#iterator()每次只從資料庫中取出少量資料,以節省記憶體
for obj in book_obj:#第一次遍歷,,列印每本書的名字
print(obj.name)
for obj in book_obj:#列印第二次,因為迭代器已經在第一次遍歷到最后了,此次遍歷不會列印
print(obj.name)
使用iterator()方法來防止生成cache,意味著遍歷同一個QuerySet時會重復執行查詢.
所以使用iterator()時,要確保代碼在操作一個大的QuerySet時沒有執行重復的迭代
5.2.6 QuerySet的cache是用于減少程式對資料庫的查詢,在通常情況下會保證在需要的時候才會查詢資料庫.
使用exists()和iterator()方法可以優化程式對記憶體的使用,但是exists()和iterator()不會生成cache,可能會造成額外的資料庫查詢
5.3 物件查詢
5.3.1 正向查找
res1=Book.objects.first()
print(res1.title)
print(res1.price)
print(res1.publish)
print(res1.publisher.name)#因為一對多的關系,所以res1.publisher是一個物件,不是一個QuerySet集合
5.3.2 反向查找
res2=Publish.objects.last()
print(res2.name)
print(res2.city)
print(res2.book_set.all())#res2.book_set是一個QuerySet集合,所以會列印集合中的所有物件元素
5.4 雙下劃線(__)查詢
5.4.1 雙下劃線(__)之單表條件查詢
例子:
table1.objects.filter(id__lt=10,id__gt=1)#獲取id小于10,且大于1的記錄
table1.objects.filter(id__in=[11,22,33,44])#獲取id在[11,22,33,44]中的記錄
table1.objects.exclude(id__in=[11,22,33,44])#獲取id不在[11,22,33,44]中的記錄
table1.objects.filter(name__contains="content1")#獲取name中包含有"contents"的記錄(區分大小寫)
table1.objects.filter(name__icontains="content1")#獲取name中包含有"content1"的記錄(不區分大小寫)
table1.objects.filter(id__range=[1,4])#獲取id在1到4(不包含4)之間的的記錄
可使用的條件:
startswith # 指定開頭的匹配條件
istartswith # 指定開頭的匹配條件(忽略大小寫)
endswith # 指定結束的匹配條件
iendswith # 指定結束的匹配條件(忽略大小寫)
5.4.2 雙下劃線(__)之多表條件查詢
正向查找(條件)之一對一查詢
#查詢書名為"python"的書的id號
res3=Book.objects.filter(title="python").values("id")
print(res3)
正向查找(條件)之一對多查詢
#查詢書名為"python"的書對應的出版社的地址
res4=Book.objects.filter(title="python").values("publisher__city")
print(res4)
#查詢"aaa"作者所寫的所有的書的名字
res5=Book.objects.filter(author__name="aaa").values("title")
print(res5)
#查詢"aaa"作者所寫的所有的書的名字(與上面的用法沒區別)
res6=Book.objects.filter(author__name="aaa").values("title")
print(res6)
反向查找之一對多查詢
#查詢出版了書名為"python"這本書的出版社的名字
res7=Publisher.objects.filter(book__title="python").values("name")
print(res7)
#查詢寫了書名為"python"的作者的名字
res8=Publisher.objects.filter(book__title="python").values("book__authors")
print(res8)
反向查找之多對多查詢
#查詢所寫的書名為"python"的作者的名字
res9=Author.objects.filter(bool__title="python").values("name")
print(res9)
條件查詢即與物件查詢對應,是指filter,values等方法中的通過__來明確查詢條件
5.4 聚合查詢和分組查詢
5.4.1 aggregate(*args,**kwargs)
通過到QuerySet進行計算,回傳一個聚合值的字典,aggregate()中的每一個引數都指定一個包含在字典中的回傳值,即在查詢集合中生成聚合
例子:
from django.db.models import Avg,Max,Min,Sum
#計算所有書籍的平均價格,書籍最高的價格和最低價格
res1=Book.objects.all().aggregate(Avg("price"),Max("price"),Min("price"))
print(res1)#列印為"{'price__avg':xxx,'price__max':xxx,'price__min':xxx}"
Django的查詢陳述句提供了一種方式描述所有圖書的集合
aggregate()子句的引數可以指定想要計算的聚合值.
aggregate()是QuerySet的一個終止子句,回傳一個包含一些鍵值對的字典.
字典的鍵的名稱是聚合值的識別符號,是按照欄位和聚合函式的名稱自動生成出來的.
字典的值是計算出來的聚合值.
可以為聚合值指定一個名稱.
#計算所有書籍的平均價格,并給書籍的平均價格起一個別名
res2=Book.objects.all().aggregate(average__price=Avg("price"))
print(res2)#列印為"{'average_price':xxx}"
5.4.2 annotate(*args,**kwargs)
可以通過計算查詢結果中每一個物件所關聯的物件集合,從而得出總計值(也可以是平均值或總和),即為查詢集的每一項生成聚合
#查詢作者"aaa"所寫的所有的書的名字
res3=Book.objects.filter(authors__name="aaa").values("title")
print(res3)
#查詢作者"bbb"所寫的所有的書的總價格
res4=Book.objects.filter(authors__name="bbb").aggregate(Sum("price"))
print(res4)
查詢各個作者所寫的書的總價格,就要使用分組
#查詢每個作者所寫的所有書籍的總價格
res5=Book.objects.values("authors__name").annotate(Sum("price"))
print(res5)
#查詢各個出版社所出版的書籍的總價格
res6=Book.objects.values("Publish__name").annotate(Min("price"))
print(res6)
5.5 F查詢和Q查詢
5.5.1 F查詢專門取物件中某列值的操作
#匯入F
from django.db.models import F
#把table1表中的num列中的每一個值在的基礎上加10
table1.objects.all().update(num=F("num")+10)
5.5.2 Q構建搜索條件
#匯入Q
from django.db.models import Q
Q物件可以對關鍵字引數進行封裝,從而更好的應用多個查詢
#查詢table2表中以"aaa"開頭的所有的title列
q1=table2.objects.filter(Q(title__startswith="aaa")).all()
print(q1)
Q物件可以組合使用&,|運算子,當一個運算子是用于兩個Q物件時,會產生一個新的Q物件
#查找以"aaa"開頭,或者以"bbb"結尾的所有title
Q(title__startswith="aaa") | Q(title__endswith="bbb")
Q物件可以用"~"運算子放在運算式前面表示否定,也可允許否定與不否定形式的組合
#查找以"aaa"開頭,且不以"bbb"結尾的所有title
Q(title__startswith="aaa") & ~Q(title__endswith="bbb")
Q物件可以與關鍵字引數查詢一起使用,Q物件放在關鍵字查詢引數的前面
查詢條件:
#查找以"aaa"開頭,以"bbb"結尾的title且書的id號大于4的記錄
Q(title__startswith="aaa") | Q(title__endswith="bbb"),book_id__gt=4
6. ORM之改(update,save)
6.1 使用save方法將所有屬性重新設定一遍,效率低
author1=Author.objects.get(id=3)#獲取id為3的作者物件
author1.name="jobs"#修改作者物件的名字
author1.save()#把更改寫入資料庫
6.2 使用update方法直接設定對就的屬性
Publish.objects.filter(id=2).update(name="北京出版社")
6.3 需要注意的點
update()是QuerySet物件的一個方法,get回傳的是一個model物件,其沒有update方法.
filter回傳的是一個QuerySet物件,filter里可以設定多個過濾條件
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/137700.html
標籤:Python
上一篇:02.Django-模板
