1. 執(zhí)行原始SQL語(yǔ)句
Django提供了兩種執(zhí)行原始SQL語(yǔ)句的方法 :第一種是使用Manage.row()
方法尿招,但是使用Manage.row()
方法只能使用原生的SQL查詢不能執(zhí)行刪除笼蛛、更新载慈、增加操作,第二種是通過(guò)django.db.connection
連接數(shù)據(jù)庫(kù)。再調(diào)用 connection.cursor()
獲取光標(biāo)對(duì)象。然后肄程,調(diào)用 cursor.execute(sql, [params])
執(zhí)行SQL以及執(zhí)行 cursor.fetchone()
或 cursor.fetchall()
返回結(jié)果。
1.1 Manage.row()執(zhí)行原生SQL查詢
使用Manage.row()
方法執(zhí)行原始SQL查詢选浑,返回結(jié)果是 django.db.models.query.RawQuerySet
對(duì)象實(shí)例蓝厌。這個(gè) RawQuerySet
對(duì)象實(shí)例可以像普通的 QuerySet
一樣。
舉一個(gè)例子來(lái)說(shuō)明鲜侥。假設(shè)你有以下模型:
class Person(models.Model):
first_name = models.CharField(max_length=20)
last_name = models.CharField(max_length=20)
birth_date = models.DateField(max_length=20)
然后可以執(zhí)行自定義SQL褂始,如下所示:
>>> for p in Person.objects.raw('SELECT * FROM myapp_person'):
... print(p)python
John Smith
Jane Jones
當(dāng)然,這個(gè)操作其實(shí)和Person.objects.all()
是完全一樣 描函, 但是崎苗, raw()
方法有很多其他的功能選擇,使它變得非常強(qiáng)大舀寓。
1.1.1 索引查找
raw()
支持索引操作胆数,所有如果只需要第一個(gè)查詢結(jié)果,則可以這樣編寫(xiě)代碼:
>>> first_person = Person.objects.raw('SELECT * FROM myapp_person')[0]
但是互墓,索引和切片不是在數(shù)據(jù)庫(kù)級(jí)別執(zhí)行的必尼。如果你有大量的 Person
對(duì)象,在SQL
級(jí)別限制查詢更加有效:
>>> first_person = Person.objects.raw('SELECT * FROM myapp_person LIMIT 1')[0]
1.1.2 惰性模型字段
raw()
方法查詢結(jié)果的字段是可以省略的:
>>> people = Person.objects.raw('SELECT id, first_name FROM myapp_person')
這個(gè) Person
此查詢返回的對(duì)象將是惰性的模型實(shí)例,這意味著查詢中省略的字段將按需加載判莉。例如::
>>> for p in Person.objects.raw('SELECT id, first_name FROM myapp_person'):
... print(p.first_name,
... p.last_name)
...
John Smith
Jane Jones
從表面上看豆挽,這個(gè)查詢只查詢到了id
和first_name
。但是券盅,實(shí)際上發(fā)出了3個(gè)查詢帮哈。只有名字是由raw()查詢檢索的——最后兩個(gè)名字在打印時(shí)都是按需檢索的。
只有一個(gè)字段是不能忽略的-主鍵字段锰镀。Django使用主鍵來(lái)標(biāo)識(shí)模型實(shí)例娘侍,因此它必須始終包含在原始查詢中。安 InvalidQuery
如果忘記包含主鍵泳炉,將引發(fā)異常憾筏。
1.1.3 支持格式化傳參
如果需要執(zhí)行參數(shù)化查詢,可以使用params
參數(shù)個(gè)raw()
方法傳遞參數(shù)
lname = 'Doe'
Person.objects.raw('SELECT * FROM myapp_person WHERE last_name = %s', [lname])
params
可以是參數(shù)列表或字典花鹅。你可以使用 %s
列表查詢字符串中的占位符氧腰,或 %(key)s
字典的占位符。
1.2 直接執(zhí)行自定義SQL
直接執(zhí)行自定義SQL是通過(guò)django.db.connection
連接數(shù)據(jù)庫(kù)翠胰。再調(diào)用 connection.cursor()
獲取光標(biāo)對(duì)象容贝。然后自脯,調(diào)用 cursor.execute(sql, [params])
執(zhí)行SQL以及執(zhí)行 cursor.fetchone()
或 cursor.fetchall()
返回結(jié)果之景。它的DB操作和Python DB API
下規(guī)范下cursor
對(duì)象常用接口是一樣的(類似于pymysql
操作mysql
數(shù)據(jù)庫(kù)),它比raw()
更加強(qiáng)大膏潮,可以執(zhí)行任意原生SQL語(yǔ)句锻狗。
# 使用django封裝好的connection對(duì)象,會(huì)自動(dòng)讀取settings.py中數(shù)據(jù)庫(kù)的配置信息
from django.db import connection
# 獲取游標(biāo)對(duì)象
cursor = connection.cursor()
# 拿到游標(biāo)對(duì)象后執(zhí)行sql語(yǔ)句
cursor.execute("select * from book")
# 獲取所有的數(shù)據(jù)
rows = cursor.fetchall()
# 遍歷查詢到的數(shù)據(jù)
for row in rows:
print(row)
2. 模型類的增刪改查操作
2.1添加數(shù)據(jù)
模型類添加數(shù)據(jù)到數(shù)據(jù)庫(kù)只需要實(shí)例化模型類對(duì)象焕参,在實(shí)例化的過(guò)程中傳遞需要參數(shù)(需要添加的數(shù)據(jù)值)轻纪,然后在實(shí)例化模型類之后,再調(diào)用模型的save
方法叠纷,這樣Django會(huì)自動(dòng)的將這個(gè)模型轉(zhuǎn)換成sql語(yǔ)句刻帚,然后存儲(chǔ)到數(shù)據(jù)庫(kù)中。示例代碼如下:
class Book(models.Model):
name = models.CharField(max_length=20,null=False)
desc = models.CharField(max_length=100,name='description',db_column="description1")
pub_date = models.DateTimeField(auto_now_add=True)
book = Book(name='三國(guó)演義',desc='三國(guó)英雄涩嚣!')
book.save()
在沒(méi)有調(diào)用save
方法之前崇众,后臺(tái)是不會(huì)執(zhí)行insert
(插入)SQL語(yǔ)句, save()
方法是沒(méi)有返回值的
Django
提供了一個(gè)更加方便的方法create()
方法航厚,使用create()
方法添加數(shù)據(jù)可以省略save
步驟(實(shí)質(zhì)上create()
方法源碼里幫我們調(diào)用了save
方法):
book = Book.objects.create(name='三國(guó)演義',desc='三國(guó)英雄顷歌!')
objects
屬性,Model.objects
實(shí)際上是一個(gè) Manager
對(duì)象實(shí)例幔睬, Django
規(guī)定在模型類中至少有一個(gè)默認(rèn)值 Manager
眯漩,如果你不添加自己的 Manager
,django將添加一個(gè)屬性objects
包含默認(rèn)值Manager
實(shí)例麻顶。如果你添加你自己的Manager
實(shí)例屬性赦抖,不顯示默認(rèn)屬性舱卡。 Manager
對(duì)象實(shí)例主要用于模型類的查詢操作,比如我們修改默認(rèn)屬性為book_obj
(實(shí)際開(kāi)發(fā)一般不修改):
class Book(models.Model):
name = models.CharField(max_length=20,null=False)
desc = models.CharField(max_length=100,name='description',db_column="description1")
pub_date = models.DateTimeField(auto_now_add=True)
book_obj = models.Manager()
此時(shí)使用create()
方法添加數(shù)據(jù)就變成了:
# 需要使用自定義的Manager屬性
book = Book.book_obj.create(name='三國(guó)演義',desc='三國(guó)英雄队萤!')
2.1.1 ForeignKey 和 ManyToManyField模型添加數(shù)據(jù)
更新一個(gè) ForeignKey
字段的工作方式與保存普通字段完全相同——只需將正確類型的對(duì)象分配給相關(guān)字段即可灼狰。
class User(models.Model):
username = models.CharField(max_length=20)
password = models.CharField(max_length=100)
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
author = models.ForeignKey("User",on_delete=models.CASCADE)
以上使用ForeignKey
來(lái)定義模型之間的關(guān)系。即在article
的實(shí)例中可以通過(guò)author
屬性來(lái)操作對(duì)應(yīng)的User
模型浮禾。這樣使用起來(lái)非常的方便交胚。示例代碼如下:
article = Article(title='abc',content='123')
author = User(username='張三',password='111111')
article.author = author
article.save()
# 修改article.author上的值
article.author.username = '李四'
article.save()
更新一個(gè)ManyToManyField
工作方式有點(diǎn)不同——使用 add()
方法來(lái)向關(guān)系中添加記錄:
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
tags = models.ManyToManyField("Tag",related_name="articles")
class Tag(models.Model):
name = models.CharField(max_length=50)
以上使用ManyToManyField
來(lái)定義模型之間的關(guān)系:
tag = Tag.objects.filter(name='娛樂(lè)').first()
article = Article(title='python入門(mén)',content='xxxxxxxxxxxxxxxxxx....')
article.tags.add(tag)
article
必須是模型類對(duì)象,不能是QuerySet
對(duì)象盈电,被添加的對(duì)象(tag
)也必須是模型類對(duì)象蝴簇。
2.2 查詢數(shù)據(jù)
Django
使用Manager
對(duì)象實(shí)例來(lái)執(zhí)行模型類的查詢操作,模型類中至少有一個(gè)默認(rèn)值 Manager匆帚,模型類名.objects
就是模型類默認(rèn)的Manager
對(duì)象實(shí)例熬词, 查詢一般就是使用filter
、exclude
以及get
三個(gè)方法來(lái)實(shí)現(xiàn)吸重。我們可以在調(diào)用這些方法的時(shí)候傳遞不同的參數(shù)來(lái)實(shí)現(xiàn)查詢需求互拾。在ORM
層面,這些查詢條件都是使用field
+__
+condition
的方式來(lái)使用的嚎幸。
查找所有數(shù)據(jù):
要查找Book這個(gè)模型對(duì)應(yīng)的表下的所有數(shù)據(jù)颜矿。那么示例代碼如下:
books = Book.objects.all()
以上將返回Book模型下的所有數(shù)據(jù)。
數(shù)據(jù)過(guò)濾:
在查找數(shù)據(jù)的時(shí)候嫉晶,有時(shí)候需要對(duì)一些數(shù)據(jù)進(jìn)行過(guò)濾骑疆。那么這時(shí)候需要調(diào)用objects
的filter
方法。實(shí)例代碼如下:
books = Book.objects.filter(name='三國(guó)演義')
> [<Book:三國(guó)演義>]
# 多個(gè)條件
books = Book.objects.filter(name='三國(guó)演義',desc='test')
調(diào)用filter
替废,會(huì)將所有滿足條件的模型對(duì)象都返回箍铭。
獲取單個(gè)對(duì)象:
使用filter
返回的是所有滿足條件的結(jié)果集。有時(shí)候如果只需要返回第一個(gè)滿足條件的對(duì)象椎镣。那么可以使用get
方法诈火。示例代碼如下:
book = Book.objects.get(name='三國(guó)演義')
> <Book:三國(guó)演義>
當(dāng)然,如果沒(méi)有找到滿足條件的對(duì)象状答,那么就會(huì)拋出一個(gè)異常冷守。而filter
在沒(méi)有找到滿足條件的數(shù)據(jù)的時(shí)候,是返回一個(gè)空的列表剪况。
數(shù)據(jù)排序:
在之前的例子中教沾,數(shù)據(jù)都是無(wú)序的。如果你想在查找數(shù)據(jù)的時(shí)候使用某個(gè)字段來(lái)進(jìn)行排序译断,那么可以使用order_by
方法來(lái)實(shí)現(xiàn)授翻。示例代碼如下:
books = Book.objects.order_by("pub_date")
以上代碼在提取所有書(shū)籍的數(shù)據(jù)的時(shí)候,將會(huì)使用pub_date
從小到大進(jìn)行排序。如果想要進(jìn)行倒序排序堪唐,那么可以在pub_date
前面加一個(gè)負(fù)號(hào)巡语。實(shí)例代碼如下:
books = Book.objects.order_by("-pub_date")
2.2.1 查詢條件
字段查找就是如何指定SQL
的主要部分的WHERE
條件。下面是常用的查詢條件的使用:
-
exact
:使用精確的=
進(jìn)行查找淮菠。如果提供的是一個(gè)None
男公,那么在SQL
層面就是被解釋為NULL
。示例代碼如下:
article = Article.objects.get(id__exact=14)
article = Article.objects.get(id__exact=None)
? 以上的兩個(gè)查找在翻譯為SQL
語(yǔ)句為如下:
select ... from article where id=14;
select ... from article where id IS NULL;
-
iexact
:使用like
進(jìn)行查找合陵。示例代碼如下:
article = Article.objects.filter(title__iexact='hello world')
? 那么以上的查詢就等價(jià)于以下的SQL
語(yǔ)句:
select ... from article where title like 'hello world';
? 注意上面這個(gè)sql
語(yǔ)句枢赔,因?yàn)樵?code>MySQL中,沒(méi)有一個(gè)叫做ilike
的拥知。所以exact
和iexact
的區(qū)別實(shí)際上就是 LIKE
和=
的區(qū)別踏拜,在大部分collation=utf8_general_ci
情況下都是一樣的(collation
是用來(lái)對(duì)字符串比較的)。
-
contains
:大小寫(xiě)敏感低剔,判斷某個(gè)字段是否包含了某個(gè)數(shù)據(jù)速梗。示例代碼如下:
articles = Article.objects.filter(title__contains='hello')
? 在翻譯成SQL
語(yǔ)句為如下:
select ... where title like binary '%hello%';
? 要注意的是,在使用contains
的時(shí)候襟齿,翻譯成的sql
語(yǔ)句左右兩邊是有百分號(hào)的姻锁,意味著使用的是模糊查詢。 而exact
翻譯成sql
語(yǔ)句左右兩邊是沒(méi)有百分號(hào)的猜欺,意味著使用的是精確的查詢位隶。
-
icontains
:大小寫(xiě)不敏感的匹配查詢。示例代碼如下:
articles = Article.objects.filter(title__icontains='hello')
? 在翻譯成SQL
語(yǔ)句為如下:
select ... where title like '%hello%';
-
in
:提取那些給定的field
的值是否在給定的容器中替梨。容器可以為list
钓试、tuple
或者任何一個(gè)可以迭代的對(duì)象,包括QuerySet
對(duì)象副瀑。示例代碼如下:
articles = Article.objects.filter(id__in=[1,2,3])
? 以上代碼在翻譯成SQL
語(yǔ)句為如下:
select ... where id in (1,3,4)
? 當(dāng)然也可以傳遞一個(gè)QuerySet
對(duì)象進(jìn)去。示例代碼如下:
inner_qs = Article.objects.filter(title__contains='hello')
categories = Category.objects.filter(article__in=inner_qs)
? 以上代碼的意思是獲取那些文章標(biāo)題包含hello
的所有分類恋谭。
? 將翻譯成以下SQL
語(yǔ)句糠睡,示例代碼如下:
select ...from category where article.id in (select id from article where title like '%hello%');
-
gt
:某個(gè)field
的值要大于給定的值。示例代碼如下:
articles = Article.objects.filter(id__gt=4)
? 以上代碼的意思是將所有id
大于4的文章全部都找出來(lái)疚颊。
? 將翻譯成以下SQL
語(yǔ)句:
select ... where id > 4;
gte
:類似于gt
狈孔,是大于等于。lt
:類似于gt
是小于材义。lte
:類似于lt
均抽,是小于等于。startswith
:判斷某個(gè)字段的值是否是以某個(gè)值開(kāi)始的其掂。大小寫(xiě)敏感油挥。示例代碼如下:
articles = Article.objects.filter(title__startswith='hello')
? 以上代碼的意思是提取所有標(biāo)題以hello
字符串開(kāi)頭的文章。
? 將翻譯成以下SQL
語(yǔ)句:
select ... where title like 'hello%'
istartswith
:類似于startswith
,但是大小寫(xiě)是不敏感的深寥。endswith
:判斷某個(gè)字段的值是否以某個(gè)值結(jié)束攘乒。大小寫(xiě)敏感。示例代碼如下:
articles = Article.objects.filter(title__endswith='world')
? 以上代碼的意思是提取所有標(biāo)題以world
結(jié)尾的文章惋鹅。
? 將翻譯成以下SQL
語(yǔ)句:
select ... where title like '%world';
iendswith
:類似于endswith
则酝,只不過(guò)大小寫(xiě)不敏感。range
:判斷某個(gè)field
的值是否在給定的區(qū)間中闰集。示例代碼如下:
from django.utils.timezone import make_aware
from datetime import datetime
start_date = make_aware(datetime(year=2018,month=1,day=1))
end_date = make_aware(datetime(year=2018,month=3,day=29,hour=16))
articles = Article.objects.filter(pub_date__range=(start_date,end_date))
? 以上代碼的意思是提取所有發(fā)布時(shí)間在2018/1/1
到2018/12/12
之間的文章沽讹。
? 將翻譯成以下的SQL
語(yǔ)句:
select ... from article where pub_time between '2018-01-01' and '2018-12-12'。
需要注意的是武鲁,以上提取數(shù)據(jù)妥泉,不會(huì)包含最后一個(gè)值。也就是不會(huì)包含2018/12/12
的文章洞坑。
而且另外一個(gè)重點(diǎn)盲链,因?yàn)槲覀冊(cè)?code>settings.py中指定了USE_TZ=True
,并且設(shè)置了TIME_ZONE='Asia/Shanghai'
迟杂,因此我們?cè)谔崛?shù)據(jù)的時(shí)候要使用django.utils.timezone.make_aware
先將datetime.datetime
從navie
時(shí)間轉(zhuǎn)換為aware
時(shí)間刽沾。make_aware
會(huì)將指定的時(shí)間轉(zhuǎn)換為TIME_ZONE
中指定的時(shí)區(qū)的時(shí)間。
-
date
:針對(duì)某些date
或者datetime
類型的字段排拷〔嗬欤可以指定date
的范圍。并且這個(gè)時(shí)間過(guò)濾监氢,還可以使用鏈?zhǔn)秸{(diào)用布蔗。示例代碼如下:
articles = Article.objects.filter(pub_date__date=date(2018,3,29))
? 以上代碼的意思是查找時(shí)間為2018/3/29
這一天發(fā)表的所有文章。
? 將翻譯成以下的sql
語(yǔ)句:
select ... WHERE DATE(CONVERT_TZ(`front_article`.`pub_date`, 'UTC', 'Asia/Shanghai')) = 2018-03-29
注意浪腐,因?yàn)槟J(rèn)情況下MySQL
的表中是沒(méi)有存儲(chǔ)時(shí)區(qū)相關(guān)的信息的纵揍。因此我們需要下載一些時(shí)區(qū)表的文件,然后添加到Mysql
的配置路徑中议街。如果你用的是windows
操作系統(tǒng)泽谨。那么在http://dev.mysql.com/downloads/timezones.html
下載timezone_2018d_posix.zip - POSIX standard
。然后將下載下來(lái)的所有文件拷貝到C:\ProgramData\MySQL\MySQL Server 5.7\Data\mysql
中特漩,如果提示文件名重復(fù)吧雹,那么選擇覆蓋即可。
如果用的是linux
或者mac
系統(tǒng)涂身,那么在命令行中執(zhí)行以下命令:mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -D mysql -u root -p
雄卷,然后輸入密碼,從系統(tǒng)中加載時(shí)區(qū)文件更新到mysql
中蛤售。
-
year
:根據(jù)年份進(jìn)行查找丁鹉。示例代碼如下:
articles = Article.objects.filter(pub_date__year=2018)
articles = Article.objects.filter(pub_date__year__gte=2017)
以上的代碼在翻譯成SQL
語(yǔ)句為如下:
select ... where pub_date between '2018-01-01' and '2018-12-31';
select ... where pub_date >= '2017-01-01';
month
:同year
妒潭,根據(jù)月份進(jìn)行查找。day
:同year
鳄炉,根據(jù)日期進(jìn)行查找杜耙。week_day
:同year
,根據(jù)星期幾進(jìn)行查找拂盯。1表示星期天佑女,7表示星期六,2-6
代表的是星期一到星期五谈竿。time
:根據(jù)時(shí)間進(jìn)行查找团驱。示例代碼如下:
articles = Article.objects.filter(pub_date__time=datetime.time(12,12,12));
? 以上的代碼是獲取每一天中12點(diǎn)12分12秒發(fā)表的所有文章。
? 更多的關(guān)于時(shí)間的過(guò)濾空凸,請(qǐng)參考Django官方文檔 嚎花。
-
isnull
:根據(jù)值是否為空進(jìn)行查找。示例代碼如下:
articles = Article.objects.filter(pub_date__isnull=False)
以上的代碼的意思是獲取所有發(fā)布日期不為空的文章呀洲。
將來(lái)翻譯成SQL
語(yǔ)句如下:
select ... where pub_date is not null;
-
regex
和iregex
:大小寫(xiě)敏感和大小寫(xiě)不敏感的正則表達(dá)式紊选。示例代碼如下:
articles = Article.objects.filter(title__regex=r'^hello')
以上代碼的意思是提取所有標(biāo)題以hello
字符串開(kāi)頭的文章。
將翻譯成以下的SQL
語(yǔ)句:
select ... where title regexp binary '^hello';
iregex
是大小寫(xiě)不敏感的道逗。
根據(jù)關(guān)聯(lián)的表進(jìn)行查詢:
假如現(xiàn)在有兩個(gè)ORM
模型兵罢,一個(gè)是Article
,一個(gè)是Category
滓窍。代碼如下:
class Category(models.Model):
"""文章分類表"""
name = models.CharField(max_length=100)
class Article(models.Model):
"""文章表"""
title = models.CharField(max_length=100,null=True)
category = models.ForeignKey("Category",on_delete=models.CASCADE)
比如想要獲取文章標(biāo)題中包含"hello
"的所有的分類卖词。那么可以通過(guò)以下代碼來(lái)實(shí)現(xiàn):
categories = Category.object.filter(article__title__contains("hello"))
2.2.2 聚合函數(shù)
如果你用原生SQL稚瘾,則可以使用聚合函數(shù)來(lái)提取數(shù)據(jù)额港。比如提取某個(gè)商品銷售的數(shù)量鳖悠,那么可以使用Count
瓮增,如果想要知道商品銷售的平均價(jià)格,那么可以使用Avg
脱惰。
聚合函數(shù)是通過(guò)aggregate
方法來(lái)實(shí)現(xiàn)的双絮。在講解這些聚合函數(shù)的用法的時(shí)候之剧,都是基于以下的模型對(duì)象來(lái)實(shí)現(xiàn)的杠园。
from django.db import models
class Author(models.Model):
"""作者模型"""
name = models.CharField(max_length=100)
age = models.IntegerField()
email = models.EmailField()
class Meta:
db_table = 'author'
class Publisher(models.Model):
"""出版社模型"""
name = models.CharField(max_length=300)
class Meta:
db_table = 'publisher'
class Book(models.Model):
"""圖書(shū)模型"""
name = models.CharField(max_length=300)
pages = models.IntegerField()
price = models.FloatField()
rating = models.FloatField()
author = models.ForeignKey(Author,on_delete=models.CASCADE)
publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
class Meta:
db_table = 'book'
class BookOrder(models.Model):
"""圖書(shū)訂單模型"""
book = models.ForeignKey("Book",on_delete=models.CASCADE)
price = models.FloatField()
class Meta:
db_table = 'book_order'
-
Avg
:求平均值顾瞪。比如想要獲取所有圖書(shū)的價(jià)格平均值。那么可以使用以下代碼實(shí)現(xiàn)抛蚁。from django.db.models import Avg result = Book.objects.aggregate(Avg('price')) print(result)
以上的打印結(jié)果是:
{"price__avg":23.0}
其中
price__avg
的結(jié)構(gòu)是根據(jù)field__avg
規(guī)則構(gòu)成的。如果想要修改默認(rèn)的名字惕橙,那么可以將Avg
賦值給一個(gè)關(guān)鍵字參數(shù)瞧甩。示例代碼如下:from django.db.models import Avg result = Book.objects.aggregate(my_avg=Avg('price')) print(result)
那么以上的結(jié)果打印為:
{"my_avg":23}
-
Count
:獲取指定的對(duì)象的個(gè)數(shù)。示例代碼如下:from django.db.models import Count result = Book.objects.aggregate(book_num=Count('id'))
以上的
result
將返回Book
表中總共有多少本圖書(shū)弥鹦。
Count
類中肚逸,還有另外一個(gè)參數(shù)叫做distinct
爷辙,默認(rèn)是等于False
,如果是等于True
朦促,那么將去掉那些重復(fù)的值膝晾。比如要獲取作者表中所有的不重復(fù)的郵箱總共有多少個(gè),那么可以通過(guò)以下代碼來(lái)實(shí)現(xiàn):from djang.db.models import Count result = Author.objects.aggregate(count=Count('email',distinct=True))
-
Max
和Min
:獲取指定對(duì)象的最大值和最小值务冕。比如想要獲取Author
表中血当,最大的年齡和最小的年齡分別是多少。那么可以通過(guò)以下代碼來(lái)實(shí)現(xiàn):from django.db.models import Max,Min result = Author.objects.aggregate(Max('age'),Min('age'))
如果最大的年齡是88,最小的年齡是18禀忆。那么以上的result將為:
{"age__max":88,"age__min":18}
-
Sum
:求指定對(duì)象的總和臊旭。比如要求圖書(shū)的銷售總額。那么可以使用以下代碼實(shí)現(xiàn):from djang.db.models import Sum result = Book.objects.annotate(total=Sum("bookstore__price")).values("name","total")
以上的代碼
annotate
的意思是給Book
表在查詢的時(shí)候添加一個(gè)字段叫做total
箩退,這個(gè)字段的數(shù)據(jù)來(lái)源是從BookStore
模型的price
的總和而來(lái)离熏。values
方法是只提取name
和total
兩個(gè)字段的值。 Variance
:求指定對(duì)象的方差戴涝。StdDev
:求指定對(duì)象的標(biāo)準(zhǔn)差滋戳。
2.2.3 F表達(dá)式和Q表達(dá)式
F表達(dá)式
F表達(dá)式是用來(lái)優(yōu)化ORM操作數(shù)據(jù)庫(kù)的。比如我們要將公司所有員工的薪水都增加1000元啥刻,如果按照正常的流程奸鸯,應(yīng)該是先從數(shù)據(jù)庫(kù)中提取所有的員工工資到Python內(nèi)存中,然后使用Python代碼在員工工資的基礎(chǔ)之上增加1000元郑什,最后再保存到數(shù)據(jù)庫(kù)中府喳。這里面涉及的流程就是,首先從數(shù)據(jù)庫(kù)中提取數(shù)據(jù)到Python內(nèi)存中蘑拯,然后在Python內(nèi)存中做完運(yùn)算钝满,之后再保存到數(shù)據(jù)庫(kù)中。示例代碼如下:
employees = Employee.objects.all()
for employee in employees:
employee.salary += 1000
employee.save()
而我們的F表達(dá)式就可以優(yōu)化這個(gè)流程申窘,他可以不需要先把數(shù)據(jù)從數(shù)據(jù)庫(kù)中提取出來(lái)弯蚜,計(jì)算完成后再保存回去,他可以直接執(zhí)行SQL語(yǔ)句剃法,就將員工的工資增加1000元碎捺。示例代碼如下:
from djang.db.models import F
Employee.object.update(salary=F("salary")+1000)
F表達(dá)式并不會(huì)馬上從數(shù)據(jù)庫(kù)中獲取數(shù)據(jù),而是在生成SQL語(yǔ)句的時(shí)候贷洲,動(dòng)態(tài)的獲取傳給F表達(dá)式的值收厨。
比如如果想要獲取作者中,name
和email
相同的作者數(shù)據(jù)优构。如果不使用F表達(dá)式诵叁,那么需要使用以下代碼來(lái)完成:
authors = Author.objects.all()
for author in authors:
if author.name == author.email:
print(author)
如果使用F表達(dá)式,那么一行代碼就可以搞定钦椭。示例代碼如下:
from django.db.models import F
authors = Author.objects.filter(name=F("email"))
Q表達(dá)式
如果想要實(shí)現(xiàn)所有價(jià)格高于100元拧额,并且評(píng)分達(dá)到9.0以上評(píng)分的圖書(shū)碑诉。那么可以通過(guò)以下代碼來(lái)實(shí)現(xiàn):
books = Book.objects.filter(price__gte=100,rating__gte=9)
以上這個(gè)案例是一個(gè)并集查詢,可以簡(jiǎn)單的通過(guò)傳遞多個(gè)條件進(jìn)去來(lái)實(shí)現(xiàn)侥锦。
但是如果想要實(shí)現(xiàn)一些復(fù)雜的查詢語(yǔ)句进栽,比如要查詢所有價(jià)格低于10元,或者是評(píng)分低于9分的圖書(shū)恭垦。那就沒(méi)有辦法通過(guò)傳遞多個(gè)條件進(jìn)去實(shí)現(xiàn)了快毛。這時(shí)候就需要使用Q表達(dá)式來(lái)實(shí)現(xiàn)了。示例代碼如下:
from django.db.models import Q
books = Book.objects.filter(Q(price__lte=10) | Q(rating__lte=9))
以上是進(jìn)行或運(yùn)算署照,當(dāng)然還可以進(jìn)行其他的運(yùn)算祸泪,比如有&
和~
(非)等。一些用Q表達(dá)式的例子如下:
from django.db.models import Q
# 獲取id等于3的圖書(shū)
books = Book.objects.filter(Q(id=3))
# 獲取id等于3建芙,或者名字中包含文字"記"的圖書(shū)
books = Book.objects.filter(Q(id=3)|Q(name__contains("記")))
# 獲取價(jià)格大于100没隘,并且書(shū)名中包含"記"的圖書(shū)
books = Book.objects.filter(Q(price__gte=100)&Q(name__contains("記")))
# 獲取書(shū)名包含“記”,但是id不等于3的圖書(shū)
books = Book.objects.filter(Q(name__contains='記') & ~Q(id=3))
2.3 QuerySet API
我們通常做查詢操作的時(shí)候禁荸,都是通過(guò)模型名字.objects
的方式進(jìn)行操作右蒲。其實(shí)模型名字.objects
是一個(gè)django.db.models.manager.Manager
對(duì)象,而Manager
這個(gè)類是一個(gè)“空殼”的類赶熟,他本身是沒(méi)有任何的屬性和方法的瑰妄。他的方法全部都是通過(guò)Python
動(dòng)態(tài)添加的方式,從QuerySet
類中拷貝過(guò)來(lái)的映砖。示例圖如下:
所以我們?nèi)绻胍獙W(xué)習(xí)
ORM
模型的查找操作间坐,必須首先要學(xué)會(huì)QuerySet
上的一些API
的使用。
2.3.1 返回新的QuerySet
的方法
在使用QuerySet
進(jìn)行查找操作的時(shí)候邑退,可以提供多種操作竹宋。比如過(guò)濾完后還要根據(jù)某個(gè)字段進(jìn)行排序,那么這一系列的操作我們可以通過(guò)一個(gè)非常流暢的鏈?zhǔn)秸{(diào)用
的方式進(jìn)行地技。比如要從文章表中獲取標(biāo)題為123
蜈七,并且提取后要將結(jié)果根據(jù)發(fā)布的時(shí)間進(jìn)行排序,那么可以使用以下方式來(lái)完成:
articles = Article.objects.filter(title='123').order_by('create_time')
可以看到order_by
方法是直接在filter
執(zhí)行后調(diào)用的莫矗。這說(shuō)明filter
返回的對(duì)象是一個(gè)擁有order_by
方法的對(duì)象飒硅。而這個(gè)對(duì)象正是一個(gè)新的QuerySet
對(duì)象。因此可以使用order_by
方法作谚。
那么以下將介紹在那些會(huì)返回新的QuerySet
對(duì)象的方法三娩。
filter
:將滿足條件的數(shù)據(jù)提取出來(lái),返回一個(gè)新的QuerySet
妹懒。具體的filter
可以提供什么條件查詢尽棕。請(qǐng)見(jiàn)查詢操作章節(jié)。-
exclude
:排除滿足條件的數(shù)據(jù)彬伦,返回一個(gè)新的QuerySet
滔悉。示例代碼如下:Article.objects.exclude(title__contains='hello')
以上代碼的意思是提取那些標(biāo)題不包含
hello
的圖書(shū)。 -
annotate
:給QuerySet
中的每個(gè)對(duì)象都添加一個(gè)使用查詢表達(dá)式(聚合函數(shù)单绑、F表達(dá)式回官、Q表達(dá)式、Func表達(dá)式等)的新字段搂橙。示例代碼如下:articles = Article.objects.annotate(author_name=F("author__name"))
以上代碼將在每個(gè)對(duì)象中都添加一個(gè)
author__name
的字段歉提,用來(lái)顯示這個(gè)文章的作者的年齡。 -
order_by
:指定將查詢的結(jié)果根據(jù)某個(gè)字段進(jìn)行排序区转。如果要倒敘排序苔巨,那么可以在這個(gè)字段的前面加一個(gè)負(fù)號(hào)。示例代碼如下:# 根據(jù)創(chuàng)建的時(shí)間正序排序 articles = Article.objects.order_by("create_time") # 根據(jù)創(chuàng)建的時(shí)間倒序排序 articles = Article.objects.order_by("-create_time") # 根據(jù)作者的名字進(jìn)行排序 articles = Article.objects.order_by("author__name") # 首先根據(jù)創(chuàng)建的時(shí)間進(jìn)行排序废离,如果時(shí)間相同侄泽,則根據(jù)作者的名字進(jìn)行排序 articles = Article.objects.order_by("create_time",'author__name')
一定要注意的一點(diǎn)是,多個(gè)
order_by
蜻韭,會(huì)把前面排序的規(guī)則給打亂悼尾,而使用后面的排序方式。比如以下代碼:articles = Article.objects.order_by("create_time").order_by("author__name")
他會(huì)根據(jù)作者的名字進(jìn)行排序肖方,而不是使用文章的創(chuàng)建時(shí)間闺魏。
-
values
:用來(lái)指定在提取數(shù)據(jù)出來(lái),需要提取哪些字段俯画。默認(rèn)情況下會(huì)把表中所有的字段全部都提取出來(lái)析桥,可以使用values
來(lái)進(jìn)行指定,并且使用了values
方法后艰垂,提取出的QuerySet
中的數(shù)據(jù)類型不是模型泡仗,而是在values
方法中指定的字段和值形成的字典:articles = Article.objects.values("title",'content') for article in articles: print(article)
以上打印出來(lái)的
article
是類似于{"title":"abc","content":"xxx"}
的形式。
如果在values
中沒(méi)有傳遞任何參數(shù)材泄,那么將會(huì)返回這個(gè)惡模型中所有的屬性沮焕。 -
values_list
:類似于values
。只不過(guò)返回的QuerySet
中拉宗,存儲(chǔ)的不是字典峦树,而是元組。示例代碼如下:articles = Article.objects.values_list("id","title") print(articles)
那么在打印
articles
后旦事,結(jié)果為``等魁巩。
如果在values_list
中只有一個(gè)字段。那么你可以傳遞flat=True
來(lái)將結(jié)果扁平化姐浮。示例代碼如下:articles1 = Article.objects.values_list("title") >> <QuerySet [("abc",),("xxx",),...]> articles2 = Article.objects.values_list("title",flat=True) >> <QuerySet ["abc",'xxx',...]>
all
:獲取這個(gè)ORM
模型的QuerySet
對(duì)象谷遂。-
select_related
:在提取某個(gè)模型的數(shù)據(jù)的同時(shí),也提前將相關(guān)聯(lián)的數(shù)據(jù)提取出來(lái)卖鲤。比如提取文章數(shù)據(jù)肾扰,可以使用select_related
將author
信息提取出來(lái)畴嘶,以后再次使用article.author
的時(shí)候就不需要再次去訪問(wèn)數(shù)據(jù)庫(kù)了〖恚可以減少數(shù)據(jù)庫(kù)查詢的次數(shù)窗悯。示例代碼如下:article = Article.objects.get(pk=1) >> article.author # 重新執(zhí)行一次查詢語(yǔ)句 article = Article.objects.select_related("author").get(pk=2) >> article.author # 不需要重新執(zhí)行查詢語(yǔ)句了
selected_related
只能用在一對(duì)多
或者一對(duì)一
中,不能用在多對(duì)多
或者多對(duì)一
中偷拔。比如可以提前獲取文章的作者蒋院,但是不能通過(guò)作者獲取這個(gè)作者的文章,或者是通過(guò)某篇文章獲取這個(gè)文章所有的標(biāo)簽莲绰。 -
prefetch_related
:這個(gè)方法和select_related
非常的類似欺旧,就是在訪問(wèn)多個(gè)表中的數(shù)據(jù)的時(shí)候,減少查詢的次數(shù)蛤签。這個(gè)方法是為了解決多對(duì)一
和多對(duì)多
的關(guān)系的查詢問(wèn)題辞友。比如要獲取標(biāo)題中帶有hello
字符串的文章以及他的所有標(biāo)簽,示例代碼如下:from django.db import connection articles = Article.objects.prefetch_related("tag_set").filter(title__contains='hello') print(articles.query) # 通過(guò)這條命令查看在底層的SQL語(yǔ)句 for article in articles: print("title:",article.title) print(article.tag_set.all()) # 通過(guò)以下代碼可以看出以上代碼執(zhí)行的sql語(yǔ)句 for sql in connection.queries: print(sql)
但是如果在使用
article.tag_set
的時(shí)候顷啼,如果又創(chuàng)建了一個(gè)新的QuerySet
那么會(huì)把之前的SQL
優(yōu)化給破壞掉踏枣。比如以下代碼:tags = Tag.obejcts.prefetch_related("articles") for tag in tags: articles = tag.articles.filter(title__contains='hello') #因?yàn)閒ilter方法會(huì)重新生成一個(gè)QuerySet,因此會(huì)破壞掉之前的sql優(yōu)化 # 通過(guò)以下代碼钙蒙,我們可以看到在使用了filter的茵瀑,他的sql查詢會(huì)更多,而沒(méi)有使用filter的躬厌,只有兩次sql查詢 for sql in connection.queries: print(sql)
那如果確實(shí)是想要在查詢的時(shí)候指定過(guò)濾條件該如何做呢马昨,這時(shí)候我們可以使用
django.db.models.Prefetch
來(lái)實(shí)現(xiàn),Prefetch
這個(gè)可以提前定義好queryset
扛施。示例代碼如下:tags = Tag.objects.prefetch_related(Prefetch("articles",queryset=Article.objects.filter(title__contains='hello'))).all() for tag in tags: articles = tag.articles.all() for article in articles: print(article) for sql in connection.queries: print('='*30) print(sql)
因?yàn)槭褂昧?code>Prefetch鸿捧,即使在查詢文章的時(shí)候使用了
filter
,也只會(huì)發(fā)生兩次查詢操作疙渣。 -
defer
:在一些表中匙奴,可能存在很多的字段,但是一些字段的數(shù)據(jù)量可能是比較龐大的妄荔,而此時(shí)你又不需要泼菌,比如我們?cè)讷@取文章列表的時(shí)候,文章的內(nèi)容我們是不需要的啦租,因此這時(shí)候我們就可以使用defer
來(lái)過(guò)濾掉一些字段哗伯。這個(gè)字段跟values
有點(diǎn)類似,只不過(guò)defer
返回的不是字典篷角,而是模型焊刹。示例代碼如下:articles = list(Article.objects.defer("title")) for sql in connection.queries: print('='*30) print(sql)
在看以上代碼的
sql
語(yǔ)句,你就可以看到,查找文章的字段虐块,除了title
俩滥,其他字段都查找出來(lái)了。當(dāng)然非凌,你也可以使用article.title
來(lái)獲取這個(gè)文章的標(biāo)題举农,但是會(huì)重新執(zhí)行一個(gè)查詢的語(yǔ)句。示例代碼如下:articles = list(Article.objects.defer("title")) for article in articles: # 因?yàn)樵谏厦嫣崛〉臅r(shí)候過(guò)濾了title # 這個(gè)地方重新獲取title敞嗡,將重新向數(shù)據(jù)庫(kù)中進(jìn)行一次查找操作 print(article.title) for sql in connection.queries: print('='*30) print(sql)
defer
雖然能過(guò)濾字段,但是有些字段是不能過(guò)濾的航背,比如id
喉悴,即使你過(guò)濾了,也會(huì)提取出來(lái)玖媚。 only
:跟defer
類似箕肃,只不過(guò)defer
是過(guò)濾掉指定的字段,而only
是只提取指定的字段今魔。get
:獲取滿足條件的數(shù)據(jù)勺像。這個(gè)函數(shù)只能返回一條數(shù)據(jù),并且如果給的條件有多條數(shù)據(jù)错森,那么這個(gè)方法會(huì)拋出MultipleObjectsReturned
錯(cuò)誤吟宦,如果給的條件沒(méi)有任何數(shù)據(jù),那么就會(huì)拋出DoesNotExit
錯(cuò)誤涩维。所以這個(gè)方法在獲取數(shù)據(jù)的只能殃姓,只能有且只有一條。-
create
:創(chuàng)建一條數(shù)據(jù)瓦阐,并且保存到數(shù)據(jù)庫(kù)中蜗侈。這個(gè)方法相當(dāng)于先用指定的模型創(chuàng)建一個(gè)對(duì)象,然后再調(diào)用這個(gè)對(duì)象的save
方法睡蟋。示例代碼如下:article = Article(title='abc') article.save() # 下面這行代碼相當(dāng)于以上兩行代碼 article = Article.objects.create(title='abc')
-
get_or_create
:根據(jù)某個(gè)條件進(jìn)行查找踏幻,如果找到了那么就返回這條數(shù)據(jù),如果沒(méi)有查找到戳杀,那么就創(chuàng)建一個(gè)该面。示例代碼如下:obj,created= Category.objects.get_or_create(title='默認(rèn)分類')
如果有標(biāo)題等于
默認(rèn)分類
的分類,那么就會(huì)查找出來(lái)豺瘤,如果沒(méi)有吆倦,則會(huì)創(chuàng)建并且存儲(chǔ)到數(shù)據(jù)庫(kù)中。
這個(gè)方法的返回值是一個(gè)元組坐求,元組的第一個(gè)參數(shù)obj
是這個(gè)對(duì)象蚕泽,第二個(gè)參數(shù)created
代表是否創(chuàng)建的。 -
bulk_create
:一次性創(chuàng)建多個(gè)數(shù)據(jù)。示例代碼如下:Tag.objects.bulk_create([ Tag(name='111'), Tag(name='222'), ])
count
:獲取提取的數(shù)據(jù)的個(gè)數(shù)须妻。如果想要知道總共有多少條數(shù)據(jù)仔蝌,那么建議使用count
,而不是使用len(articles)
這種荒吏。因?yàn)?code>count在底層是使用select count(*)
來(lái)實(shí)現(xiàn)的敛惊,這種方式比使用len
函數(shù)更加的高效。first
和last
:返回QuerySet
中的第一條和最后一條數(shù)據(jù)绰更。aggregate
:使用聚合函數(shù)瞧挤。-
exists
:判斷某個(gè)條件的數(shù)據(jù)是否存在。如果要判斷某個(gè)條件的元素是否存在儡湾,那么建議使用exists
特恬,這比使用count
或者直接判斷QuerySet
更有效得多。示例代碼如下:if Article.objects.filter(title__contains='hello').exists(): print(True) 比使用count更高效: if Article.objects.filter(title__contains='hello').count() > 0: print(True) 也比直接判斷QuerySet更高效: if Article.objects.filter(title__contains='hello'): print(True)
-
distinct
:去除掉那些重復(fù)的數(shù)據(jù)徐钠。這個(gè)方法如果底層數(shù)據(jù)庫(kù)用的是MySQL
癌刽,那么不能傳遞任何的參數(shù)。比如想要提取所有銷售的價(jià)格超過(guò)80元的圖書(shū)尝丐,并且刪掉那些重復(fù)的显拜,那么可以使用distinct
來(lái)幫我們實(shí)現(xiàn),示例代碼如下:books = Book.objects.filter(bookorder__price__gte=80).distinct()
需要注意的是爹袁,如果在
distinct
之前使用了order_by
远荠,那么因?yàn)?code>order_by會(huì)提取order_by
中指定的字段,因此再使用distinct
就會(huì)根據(jù)多個(gè)字段來(lái)進(jìn)行唯一化呢簸,所以就不會(huì)把那些重復(fù)的數(shù)據(jù)刪掉矮台。示例代碼如下:orders = BookOrder.objects.order_by("create_time").values("book_id").distinct()
那么以上代碼因?yàn)槭褂昧?code>order_by,即使使用了
distinct
根时,也會(huì)把重復(fù)的book_id
提取出來(lái)瘦赫。 -
update
:執(zhí)行更新操作,在SQL
底層走的也是update
命令蛤迎。比如要將所有category
為空的article
的article
字段都更新為默認(rèn)的分類确虱。示例代碼如下:Article.objects.filter(category__isnull=True).update(category_id=3)
注意這個(gè)方法走的是更新的邏輯。所以更新完成后保存到數(shù)據(jù)庫(kù)中不會(huì)執(zhí)行
save
方法替裆,因此不會(huì)更新auto_now
設(shè)置的字段校辩。 delete
:刪除所有滿足條件的數(shù)據(jù)。刪除數(shù)據(jù)的時(shí)候辆童,要注意on_delete
指定的處理方式宜咒。-
切片操作:有時(shí)候我們查找數(shù)據(jù),有可能只需要其中的一部分把鉴。那么這時(shí)候可以使用切片操作來(lái)幫我們完成故黑。
QuerySet
使用切片操作就跟列表使用切片操作是一樣的儿咱。示例代碼如下:books = Book.objects.all()[1:3] for book in books: print(book)
切片操作并不是把所有數(shù)據(jù)從數(shù)據(jù)庫(kù)中提取出來(lái)再做切片操作。而是在數(shù)據(jù)庫(kù)層面使用
LIMIE
和OFFSET
來(lái)幫我們完成场晶。所以如果只需要取其中一部分的數(shù)據(jù)的時(shí)候混埠,建議大家使用切片操作。
2.3.1 不返回QuerySet的方法
以下方法不使用緩存诗轻,它們每次被調(diào)用時(shí)都會(huì)查詢數(shù)據(jù)庫(kù)钳宪。
get()
-
get
(**kwargs
)
返回與給定查找參數(shù)匹配的對(duì)象,該對(duì)象應(yīng)采用中描述的格式扳炬。
get()
如果找到多個(gè)對(duì)象吏颖。就會(huì)拋出 MultipleObjectsReturned
異常,該異常是模型類的屬性鞠柄。
get()
如果沒(méi)有為給定參數(shù)找到對(duì)象侦高,則出現(xiàn)DoesNotExist
異常。此異常是模型類的屬性厌杜。例子:
Entry.objects.get(id='foo') # raises Entry.DoesNotExist
這個(gè)DoesNotExist
異常繼承自django.core.exceptions.ObjectDoesNotExist
,這樣您可以針對(duì)多個(gè) DoesNotExist
例外情況计螺。例子:
from django.core.exceptions import ObjectDoesNotExist
try:
e = Entry.objects.get(id=3)
b = Blog.objects.get(id=1)
except ObjectDoesNotExist:
print("Either the entry or blog doesn't exist.")
如果希望查詢集返回一行夯尽,則可以使用 get()
不帶任何參數(shù)返回該行的對(duì)象::
entry = Entry.objects.filter(...).exclude(...).get()
create()
-
create
(**kwargs
)
一種方便的方法,用于創(chuàng)建一個(gè)對(duì)象并將其全部保存在一個(gè)步驟中登馒。因此:
p = Person.objects.create(first_name="Bruce", last_name="Springsteen")
和:
p = Person(first_name="Bruce", last_name="Springsteen")
p.save(force_insert=True)
是等價(jià)的匙握。
這個(gè) force_insert
參數(shù)在其他地方有文檔記錄,但這意味著總是會(huì)創(chuàng)建一個(gè)新對(duì)象陈轿。通常你不需要擔(dān)心這個(gè)圈纺。但是,如果模型包含您設(shè)置的手動(dòng)主鍵值麦射,并且該值已存在于數(shù)據(jù)庫(kù)中蛾娶,則調(diào)用 create()
將失敗 IntegrityError
因?yàn)橹麈I必須是唯一的。如果您使用的是手動(dòng)主鍵潜秋,請(qǐng)準(zhǔn)備好處理異常蛔琅。
get_or_create()
-
get_or_create
(defaults=None, **kwargs)
一種方便的查找給定對(duì)象的方法 kwargs
(如果模型具有所有字段的默認(rèn)值,則可能為空)峻呛,如有必要罗售,請(qǐng)創(chuàng)建一個(gè)字段。
返回的元組 (object, created)
在哪里 object
是檢索或創(chuàng)建的對(duì)象钩述,并且 created
是一個(gè)布爾值寨躁,指定是否創(chuàng)建了新對(duì)象。
這意味著這是一個(gè)到樣板文件代碼的快捷方式牙勘。例如::
try:
obj = Person.objects.get(first_name='John', last_name='Lennon')
except Person.DoesNotExist:
obj = Person(first_name='John', last_name='Lennon', birthday=date(1940, 10, 9))
obj.save()
隨著模型中字段數(shù)的增加职恳,這種模式變得相當(dāng)復(fù)雜。上面的例子可以用重寫(xiě) get_or_create()
像這樣::
obj, created = Person.objects.get_or_create(
first_name='John',
last_name='Lennon',
defaults={'birthday': date(1940, 10, 9)},
)
傳遞給的任何關(guān)鍵字參數(shù) get_or_create()
- 除了 一個(gè)可選的調(diào)用 defaults
-將用于 get()
調(diào)用。如果找到一個(gè)對(duì)象话肖, get_or_create()
返回該對(duì)象的元組北秽,然后 False
.
可以通過(guò)鏈接為檢索到的對(duì)象指定更復(fù)雜的條件 get_or_create()
具有 filter()
并使用 Q objects
. 例如,要檢索羅伯特或鮑勃馬利(如果兩者都存在)最筒,并創(chuàng)建后者贺氓,否則:
from django.db.models import Q
obj, created = Person.objects.filter(
Q(first_name='Bob') | Q(first_name='Robert'),
).get_or_create(last_name='Marley', defaults={'first_name': 'Bob'})
如果找到多個(gè)對(duì)象, get_or_create()
加薪 MultipleObjectsReturned
. 如果對(duì)象是 not 發(fā)現(xiàn)床蜘, get_or_create()
將實(shí)例化并保存新對(duì)象禾进,返回新對(duì)象的元組 True
. 將根據(jù)以下算法大致創(chuàng)建新對(duì)象:
params = {k: v for k, v in kwargs.items() if '__' not in k}
params.update({k: v() if callable(v) else v for k, v in defaults.items()})
obj = self.model(**params)
obj.save()
在英語(yǔ)中,這意味著從任何不包含雙下劃線(表示非精確查找)的非“默認(rèn)值”關(guān)鍵字參數(shù)開(kāi)始梳玫。然后添加 defaults
谊囚,根據(jù)需要重寫(xiě)任何鍵,并將結(jié)果用作模型類的關(guān)鍵字參數(shù)丹擎。如果在 defaults
尾抑,評(píng)估它們。如上所述蒂培,這是對(duì)所用算法的簡(jiǎn)化再愈,但它包含了所有相關(guān)的細(xì)節(jié)。內(nèi)部實(shí)現(xiàn)有比這更多的錯(cuò)誤檢查护戳,并處理一些額外的邊緣條件翎冲;如果您感興趣,請(qǐng)閱讀代碼媳荒。
如果您有一個(gè)名為 defaults
并希望在 get_or_create()
只是使用 'defaults__exact'
抗悍,像這樣::
Foo.objects.get_or_create(defaults__exact='bar', defaults={'defaults': 'baz'})
這個(gè) get_or_create()
方法的錯(cuò)誤行為與 create()
當(dāng)您使用手動(dòng)指定的主鍵時(shí)。如果需要?jiǎng)?chuàng)建一個(gè)對(duì)象并且該鍵已經(jīng)存在于數(shù)據(jù)庫(kù)中钳枕,則 IntegrityError
將被提升缴渊。
這個(gè)方法是原子的,假定正確的用法么伯、正確的數(shù)據(jù)庫(kù)配置和底層數(shù)據(jù)庫(kù)的正確行為疟暖。但是,如果在數(shù)據(jù)庫(kù)級(jí)別不強(qiáng)制 kwargs
用于 get_or_create
調(diào)用(見(jiàn)) unique
或 unique_together
田柔,該方法容易出現(xiàn)競(jìng)爭(zhēng)條件俐巴,導(dǎo)致多行同時(shí)插入相同參數(shù)。
如果您使用的是MySQL硬爆,請(qǐng)確保使用 READ COMMITTED
隔離級(jí)別而不是 REPEATABLE READ
(默認(rèn))欣舵,否則您可能會(huì)看到 get_or_create
將提高 IntegrityError
但是這個(gè)物體不會(huì)出現(xiàn)在后面 get()
打電話。
最后缀磕,一句關(guān)于使用的話 get_or_create()
在Django視圖中缘圈。請(qǐng)確保只在 POST
除非你有充分的理由不這樣做劣光。 GET
請(qǐng)求不應(yīng)該對(duì)數(shù)據(jù)有任何影響。相反糟把,使用 POST
每當(dāng)對(duì)頁(yè)面的請(qǐng)求對(duì)數(shù)據(jù)有副作用時(shí)绢涡。更多,請(qǐng)參見(jiàn) Safe methods 在HTTP規(guī)范中遣疯。
警告
你可以使用 get_or_create()
通過(guò) ManyToManyField
屬性和反向關(guān)系雄可。在這種情況下,您將在該關(guān)系的上下文中限制查詢缠犀。如果你不經(jīng)常使用它数苫,可能會(huì)導(dǎo)致一些完整性問(wèn)題。
以下型號(hào):
class Chapter(models.Model):
title = models.CharField(max_length=255, unique=True)
class Book(models.Model):
title = models.CharField(max_length=256)
chapters = models.ManyToManyField(Chapter)
你可以使用 get_or_create()
通過(guò)書(shū)籍的章節(jié)字段辨液,但它只在該書(shū)的上下文中提扰凹薄:
>>> book = Book.objects.create(title="Ulysses")
>>> book.chapters.get_or_create(title="Telemachus")
(<Chapter: Telemachus>, True)
>>> book.chapters.get_or_create(title="Telemachus")
(<Chapter: Telemachus>, False)
>>> Chapter.objects.create(title="Chapter 1")
<Chapter: Chapter 1>
>>> book.chapters.get_or_create(title="Chapter 1")
# Raises IntegrityError
這是因?yàn)樗噲D通過(guò)《尤利西斯》這本書(shū)獲得或創(chuàng)造“第1章”,但它不能做到任何一個(gè):關(guān)系不能獲取那一章滔迈,因?yàn)樗c那本書(shū)無(wú)關(guān)止吁,但也不能創(chuàng)造它,因?yàn)?title
字段應(yīng)該是唯一的燎悍。
update_or_create()
-
update_or_create
(defaults=None, **kwargs)
用給定的 kwargs
赏殃,必要時(shí)創(chuàng)建新的。這個(gè) defaults
是用于更新對(duì)象的(字段间涵、值)對(duì)的字典。中的值 defaults
可以是可調(diào)用的榜揖。
返回的元組 (object, created)
在哪里 object
是創(chuàng)建或更新的對(duì)象勾哩,并且 created
是一個(gè)布爾值,指定是否創(chuàng)建了新對(duì)象举哟。
這個(gè) update_or_create
方法嘗試根據(jù)給定的 kwargs
. 如果找到匹配項(xiàng)思劳,它將更新 defaults
字典。
這意味著這是一個(gè)到樣板文件代碼的快捷方式妨猩。例如::
defaults = {'first_name': 'Bob'}
try:
obj = Person.objects.get(first_name='John', last_name='Lennon')
for key, value in defaults.items():
setattr(obj, key, value)
obj.save()
except Person.DoesNotExist:
new_values = {'first_name': 'John', 'last_name': 'Lennon'}
new_values.update(defaults)
obj = Person(**new_values)
obj.save()
隨著模型中字段數(shù)的增加潜叛,這種模式變得相當(dāng)復(fù)雜。上面的例子可以用重寫(xiě) update_or_create()
像這樣::
obj, created = Person.objects.update_or_create(
first_name='John', last_name='Lennon',
defaults={'first_name': 'Bob'},
)
有關(guān)如何傳入名稱的詳細(xì)描述 kwargs
已解決壶硅,請(qǐng)參閱 get_or_create()
.
如上所述 get_or_create()
威兜,此方法容易出現(xiàn)競(jìng)爭(zhēng)條件,如果在數(shù)據(jù)庫(kù)級(jí)別不強(qiáng)制唯一性庐椒,則可能導(dǎo)致同時(shí)插入多行椒舵。
喜歡 get_or_create()
和 create()
,如果正在使用手動(dòng)指定的主鍵约谈,并且需要?jiǎng)?chuàng)建一個(gè)對(duì)象笔宿,但該鍵已存在于數(shù)據(jù)庫(kù)中犁钟,則 IntegrityError
提高了。
bulk_create()
-
bulk_create
(objs, batch_size=None, ignore_conflicts=False)
此方法以有效的方式將提供的對(duì)象列表插入數(shù)據(jù)庫(kù)(通常只有1個(gè)查詢泼橘,不管有多少個(gè)對(duì)象)::
>>> Entry.objects.bulk_create([
... Entry(headline='This is a test'),
... Entry(headline='This is only a test'),
... ])
不過(guò)涝动,這有許多警告:
模型的
save()
將不調(diào)用方法,并且pre_save
和post_save
不會(huì)發(fā)送信號(hào)炬灭。它不適用于多表繼承方案中的子模型醋粟。
如果模型的主鍵是
AutoField
它不檢索和設(shè)置主鍵屬性,因?yàn)?save()
是的担败,除非數(shù)據(jù)庫(kù)后端支持它(目前是PostgreSQL)昔穴。它不適用于多對(duì)多關(guān)系。
-
它鑄造
objs
到一個(gè)列表提前,該列表完全評(píng)估objs
如果是發(fā)電機(jī)吗货。強(qiáng)制轉(zhuǎn)換允許檢查所有對(duì)象,以便可以首先插入具有手動(dòng)設(shè)置的主鍵的任何對(duì)象狈网。如果要批量插入對(duì)象而不同時(shí)評(píng)估整個(gè)生成器宙搬,則可以使用此技術(shù),只要對(duì)象沒(méi)有任何手動(dòng)設(shè)置的主鍵::from itertools import islice batch_size = 100 objs = (Entry(headline='Test %s' % i) for i in range(1000)) while True: batch = list(islice(objs, batch_size)) if not batch: break Entry.objects.bulk_create(batch, batch_size)
這個(gè) batch_size
參數(shù)控制在單個(gè)查詢中創(chuàng)建的對(duì)象數(shù)拓哺。默認(rèn)值是在一個(gè)批處理中創(chuàng)建所有對(duì)象勇垛,但sqlite除外,其中默認(rèn)值是每個(gè)查詢最多使用999個(gè)變量士鸥。
在支持它的數(shù)據(jù)庫(kù)(除了Oracle)上闲孤,設(shè)置 ignore_conflicts
參數(shù)到 True
告訴數(shù)據(jù)庫(kù)忽略插入任何失敗約束的行(如重復(fù)的唯一值)的失敗。啟用此參數(shù)將禁用在每個(gè)模型實(shí)例上設(shè)置主鍵(如果數(shù)據(jù)庫(kù)通常支持)烤礁。
Changed in Django 2.2:
這個(gè) ignore_conflicts
已添加參數(shù)讼积。
bulk_update()
New in Django 2.2:
-
bulk_update
(objs, fields, batch_size=None)
此方法可以有效地更新所提供模型實(shí)例上的給定字段,通常只需一個(gè)查詢:
>>> objs = [
... Entry.objects.create(headline='Entry 1'),
... Entry.objects.create(headline='Entry 2'),
... ]
>>> objs[0].headline = 'This is entry 1'
>>> objs[1].headline = 'This is entry 2'
>>> Entry.objects.bulk_update(objs, ['headline'])
QuerySet.update()
用于保存更改脚仔,因此這比遍歷模型列表和調(diào)用 save()
但有幾點(diǎn)需要注意:
- 無(wú)法更新模型的主鍵勤众。
- 每個(gè)模型
save()
未調(diào)用方法,并且pre_save
和post_save
沒(méi)有發(fā)送信號(hào)鲤脏。 - 如果更新大量行中的大量列们颜,則生成的SQL可能非常大。通過(guò)指定合適的
batch_size
. - 更新在多表繼承祖先上定義的字段將對(duì)每個(gè)祖先產(chǎn)生額外的查詢猎醇。
- 如果
objs
包含重復(fù)項(xiàng)窥突,只更新第一個(gè)。
這個(gè) batch_size
參數(shù)控制單個(gè)查詢中保存的對(duì)象數(shù)姑食。默認(rèn)情況下波岛,更新一批中的所有對(duì)象,除了對(duì)查詢中使用的變量數(shù)量有限制的sqlite和oracle音半。
count()
-
count
()
返回一個(gè)整數(shù)则拷,該整數(shù)表示數(shù)據(jù)庫(kù)中與 QuerySet
.
例子::
# Returns the total number of entries in the database.
Entry.objects.count()
# Returns the number of entries whose headline contains 'Lennon'
Entry.objects.filter(headline__contains='Lennon').count()
A count()
調(diào)用執(zhí)行 SELECT COUNT(*)
在幕后贡蓖,所以你應(yīng)該經(jīng)常使用 count()
而不是將所有記錄加載到Python對(duì)象中并調(diào)用 len()
在結(jié)果上(除非您無(wú)論如何都需要將對(duì)象加載到內(nèi)存中,在這種情況下 len()
會(huì)更快)煌茬。
請(qǐng)注意斥铺,如果需要 QuerySet
并且還從中檢索模型實(shí)例(例如,通過(guò)對(duì)其進(jìn)行迭代)坛善,使用它可能更有效 len(queryset)
這不會(huì)導(dǎo)致額外的數(shù)據(jù)庫(kù)查詢 count()
會(huì)晾蜘。
in_bulk()
-
in_bulk
(id_list=None, field_name='pk')
獲取字段值列表 (id_list
) field_name
對(duì)于這些值,并返回將每個(gè)值映射到具有給定字段值的對(duì)象實(shí)例的字典眠屎。如果 id_list
未提供剔交,將返回查詢集中的所有對(duì)象。 field_name
必須是唯一字段改衩,并且默認(rèn)為主鍵岖常。
例子::
>>> Blog.objects.in_bulk([1])
{1: <Blog: Beatles Blog>}
>>> Blog.objects.in_bulk([1, 2])
{1: <Blog: Beatles Blog>, 2: <Blog: Cheddar Talk>}
>>> Blog.objects.in_bulk([])
{}
>>> Blog.objects.in_bulk()
{1: <Blog: Beatles Blog>, 2: <Blog: Cheddar Talk>, 3: <Blog: Django Weblog>}
>>> Blog.objects.in_bulk(['beatles_blog'], field_name='slug')
{'beatles_blog': <Blog: Beatles Blog>}
如果你通過(guò) in_bulk()
一個(gè)空列表,你會(huì)得到一本空字典葫督。
iterator()
-
iterator
(chunk_size=2000)
評(píng)估 QuerySet
(通過(guò)執(zhí)行查詢)并返回迭代器(請(qǐng)參見(jiàn) PEP 234 )在結(jié)果上竭鞍。一 QuerySet
通常在內(nèi)部緩存其結(jié)果,以便重復(fù)的計(jì)算不會(huì)導(dǎo)致額外的查詢橄镜。相反偎快, iterator()
將直接讀取結(jié)果,而不在 QuerySet
級(jí)別(在內(nèi)部洽胶,默認(rèn)迭代器調(diào)用 iterator()
并緩存返回值)晒夹。對(duì)于一個(gè) QuerySet
它返回大量只需要訪問(wèn)一次的對(duì)象,這可以導(dǎo)致更好的性能和顯著的內(nèi)存減少姊氓。
注意使用 iterator()
在一 QuerySet
已評(píng)估的將強(qiáng)制它重新評(píng)估惋戏,重復(fù)查詢。
此外他膳,使用 iterator()
前因 prefetch_related()
調(diào)用將被忽略,因?yàn)檫@兩個(gè)優(yōu)化在一起沒(méi)有意義绒窑。
根據(jù)數(shù)據(jù)庫(kù)后端的不同棕孙,查詢結(jié)果將同時(shí)加載,或者使用服務(wù)器端光標(biāo)從數(shù)據(jù)庫(kù)中進(jìn)行流式傳輸些膨。
使用服務(wù)器端光標(biāo)
甲骨文與 PostgreSQL 使用服務(wù)器端游標(biāo)從數(shù)據(jù)庫(kù)流式傳輸結(jié)果蟀俊,而不將整個(gè)結(jié)果集加載到內(nèi)存中。
Oracle數(shù)據(jù)庫(kù)驅(qū)動(dòng)程序始終使用服務(wù)器端游標(biāo)订雾。
對(duì)于服務(wù)器端光標(biāo)肢预, chunk_size
參數(shù)指定要在數(shù)據(jù)庫(kù)驅(qū)動(dòng)程序級(jí)別緩存的結(jié)果數(shù)。獲取更大的塊會(huì)減少數(shù)據(jù)庫(kù)驅(qū)動(dòng)程序和數(shù)據(jù)庫(kù)之間的往返次數(shù)洼哎,但會(huì)犧牲內(nèi)存烫映。
在PostgreSQL上沼本,只有在 DISABLE_SERVER_SIDE_CURSORS
設(shè)置是 False
. 讀 事務(wù)池和服務(wù)器端游標(biāo) 如果您使用的是在事務(wù)池模式下配置的連接池。禁用服務(wù)器端游標(biāo)時(shí)锭沟,行為與不支持服務(wù)器端游標(biāo)的數(shù)據(jù)庫(kù)相同抽兆。
沒(méi)有服務(wù)器端光標(biāo)
MySQL不支持流式結(jié)果,因此python數(shù)據(jù)庫(kù)驅(qū)動(dòng)程序?qū)⒄麄€(gè)結(jié)果集加載到內(nèi)存中族淮。然后辫红,數(shù)據(jù)庫(kù)適配器使用 fetchmany()
方法定義于 PEP 249 .
sqlite可以使用 fetchmany()
但是,由于sqlite不提供連接內(nèi)查詢之間的隔離祝辣,因此在寫(xiě)入要循環(huán)訪問(wèn)的表時(shí)要小心贴妻。見(jiàn) 使用時(shí)隔離 QuerySet.iterator() 更多信息。
這個(gè) chunk_size
參數(shù)控制從數(shù)據(jù)庫(kù)驅(qū)動(dòng)程序中檢索的批次django的大小蝙斜。更大的批處理減少了與數(shù)據(jù)庫(kù)驅(qū)動(dòng)程序通信的開(kāi)銷名惩,但代價(jià)是內(nèi)存消耗略有增加。
默認(rèn)值為 chunk_size
乍炉,2000绢片,來(lái)自 a calculation on the psycopg mailing list :
假設(shè)有10-20列的行與文本和數(shù)字?jǐn)?shù)據(jù)混合,2000將獲取少于100kb的數(shù)據(jù)岛琼,這似乎是在傳輸?shù)男袛?shù)和過(guò)早退出循環(huán)時(shí)丟棄的數(shù)據(jù)之間的一個(gè)很好的折衷底循。
Changed in Django 2.2:
添加了對(duì)sqlite上的結(jié)果流的支持。
latest()
-
latest
(*fields)
基于給定字段返回表中的最新對(duì)象槐瑞。
此示例返回最新的 Entry
在表格上熙涤,根據(jù) pub_date
領(lǐng)域:
Entry.objects.latest('pub_date')
您還可以根據(jù)幾個(gè)字段選擇最新的。例如困檩,選擇 Entry
最早的 expire_date
當(dāng)兩個(gè)條目相同時(shí) pub_date
::
Entry.objects.latest('pub_date', '-expire_date')
負(fù)面簽到 '-expire_date'
排序方法 expire_date
在里面 下降的 秩序祠挫。自從 latest()
獲取最后一個(gè)結(jié)果, Entry
最早的 expire_date
被選中悼沿。
如果你的模型 Meta 指定 get_latest_by
等舔,您可以省略 earliest()
或 latest()
. 中指定的字段 get_latest_by
默認(rèn)情況下將使用。
喜歡 get()
糟趾, earliest()
和 latest()
提升 DoesNotExist
如果沒(méi)有具有給定參數(shù)的對(duì)象慌植。
注意 earliest()
和 latest()
純粹為了方便和可讀性而存在。
earliest()
和 latest()
可以返回日期為空的實(shí)例义郑。
由于將排序委托給數(shù)據(jù)庫(kù)蝶柿,因此如果使用不同的數(shù)據(jù)庫(kù),允許空值的字段的結(jié)果的排序可能會(huì)有所不同非驮。例如交汤,PostgreSQL和MySQL對(duì)空值進(jìn)行排序,就像它們高于非空值一樣劫笙,而SQLite則相反芙扎。
您可能希望篩選出空值::
Entry.objects.filter(pub_date__isnull=False).latest('pub_date')
earliest()
-
earliest
(*fields)
以其他方式工作 latest()
但方向改變了星岗。
first()
-
first
()
返回與查詢集匹配的第一個(gè)對(duì)象,或者 None
如果沒(méi)有匹配的對(duì)象纵顾。如果 QuerySet
未定義排序伍茄,則按主鍵自動(dòng)排序查詢集。這可能會(huì)影響聚合結(jié)果施逾,如中所述 與默認(rèn)排序或 order_by() .
例子::
p = Article.objects.order_by('title', 'pub_date').first()
注意 first()
是一種方便的方法敷矫,下面的代碼示例等效于上面的示例:
try:
p = Article.objects.order_by('title', 'pub_date')[0]
except IndexError:
p = None
last()
-
last
()
作品像 first()
,但返回查詢集中的最后一個(gè)對(duì)象汉额。
aggregate()
-
aggregate
(*args, **kwargs)
返回計(jì)算的聚合值(平均值曹仗、和等)的字典 QuerySet
. 每個(gè)參數(shù) aggregate()
指定將包含在返回的字典中的值。
Django提供的聚合函數(shù)在 Aggregation Functions 下面蠕搜。因?yàn)榧弦彩?query expressions 可以將聚合與其他聚合或值組合以創(chuàng)建復(fù)雜聚合怎茫。
使用關(guān)鍵字參數(shù)指定的聚合將使用關(guān)鍵字作為批注的名稱。匿名參數(shù)將根據(jù)聚合函數(shù)的名稱和要聚合的模型字段為其生成一個(gè)名稱妓灌。復(fù)雜聚合不能使用匿名參數(shù)轨蛤,必須將關(guān)鍵字參數(shù)指定為別名。
例如虫埂,當(dāng)您處理博客條目時(shí)祥山,您可能希望知道貢獻(xiàn)博客條目的作者數(shù):
>>> from django.db.models import Count
>>> q = Blog.objects.aggregate(Count('entry'))
{'entry__count': 16}
通過(guò)使用關(guān)鍵字參數(shù)指定聚合函數(shù),可以控制返回的聚合值的名稱::
>>> q = Blog.objects.aggregate(number_of_entries=Count('entry'))
{'number_of_entries': 16}
有關(guān)聚合的深入討論掉伏,請(qǐng)參見(jiàn) the topic guide on Aggregation .
exists()
-
exists
()
返回 True
如果 QuerySet
包含任何結(jié)果缝呕,以及 False
如果不是。它試圖以最簡(jiǎn)單和最快的方式執(zhí)行查詢斧散,但是 does 執(zhí)行與普通查詢幾乎相同的查詢 QuerySet
查詢供常。
exists()
對(duì)于與中的兩個(gè)對(duì)象成員關(guān)系相關(guān)的搜索很有用 QuerySet
以及任何對(duì)象的存在 QuerySet
尤其是在大 QuerySet
.
找到具有唯一字段的模型(例如 primary_key
)是的成員 QuerySet
是::
entry = Entry.objects.get(pk=123)
if some_queryset.filter(pk=entry.pk).exists():
print("Entry contained in queryset")
這將比以下更快,后者需要對(duì)整個(gè)查詢集進(jìn)行計(jì)算和迭代:
if entry in some_queryset:
print("Entry contained in QuerySet")
要查找查詢集是否包含任何項(xiàng):
if some_queryset.exists():
print("There is at least one object in some_queryset")
速度比:
if some_queryset:
print("There is at least one object in some_queryset")
…但不是很大程度上(因此需要一個(gè)大型的查詢集來(lái)提高效率)鸡捐。
此外栈暇,如果 some_queryset
尚未評(píng)估,但您知道它將在某個(gè)時(shí)間點(diǎn)箍镜,然后使用 some_queryset.exists()
將比簡(jiǎn)單地使用 bool(some_queryset)
瞻鹏,它檢索結(jié)果,然后檢查是否返回了任何結(jié)果鹿寨。
update()
-
update
(**kwargs)
對(duì)指定的字段執(zhí)行SQL更新查詢,并返回匹配的行數(shù)(如果某些行已有新值薪夕,則可能不等于更新的行數(shù))脚草。
例如,要關(guān)閉2010年發(fā)布的所有日志的評(píng)論原献,可以執(zhí)行以下操作:
>>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False)
(這假設(shè) Entry
模型有字段 pub_date
和 comments_on
)
您可以更新多個(gè)字段-數(shù)量沒(méi)有限制馏慨。例如埂淮,這里我們更新 comments_on
和 headline
領(lǐng)域::
>>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False, headline='This is old')
這個(gè) update()
方法被立即應(yīng)用,并且對(duì) QuerySet
它只能更新模型主表中的列写隶,而不能更新相關(guān)模型上的列倔撞。您不能這樣做,例如:
>>> Entry.objects.update(blog__name='foo') # Won't work!
基于相關(guān)字段的篩選仍然是可能的慕趴,但是:
>>> Entry.objects.filter(blog__id=1).update(comments_on=True)
你不能調(diào)用 update()
在一 QuerySet
已經(jīng)取了一個(gè)切片或不能再過(guò)濾的痪蝇。
這個(gè) update()
方法返回受影響的行數(shù)::
>>> Entry.objects.filter(id=64).update(comments_on=True)
1
>>> Entry.objects.filter(slug='nonexistent-slug').update(comments_on=True)
0
>>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False)
132
如果您只是更新一個(gè)記錄,而不需要對(duì)模型對(duì)象做任何事情冕房,那么最有效的方法是調(diào)用 update()
而不是將模型對(duì)象加載到內(nèi)存中躏啰。例如,不要這樣做:
e = Entry.objects.get(id=10)
e.comments_on = False
e.save()
…這樣做:
Entry.objects.filter(id=10).update(comments_on=False)
使用 update()
還可以防止在加載對(duì)象和調(diào)用之間的短時(shí)間內(nèi)耙册,數(shù)據(jù)庫(kù)中的某些內(nèi)容可能發(fā)生變化的爭(zhēng)用情況给僵。 save()
.
最后,要意識(shí)到 update()
在SQL級(jí)別進(jìn)行更新详拙,因此不調(diào)用任何 save()
方法帝际,它也不會(huì)發(fā)出 pre_save
或 post_save
信號(hào)(是調(diào)用的結(jié)果 Model.save()
)如果要為具有自定義 save()
方法,循環(huán)它們并調(diào)用 save()
饶辙,像這樣:
for e in Entry.objects.filter(pub_date__year=2010):
e.comments_on = False
e.save()
delete()
-
delete
()
對(duì)中的所有行執(zhí)行SQL刪除查詢 QuerySet
并返回刪除的對(duì)象數(shù)和一個(gè)字典蹲诀,其中包含每個(gè)對(duì)象類型的刪除數(shù)。
這個(gè) delete()
立即應(yīng)用畸悬。你不能調(diào)用 delete()
在一 QuerySet
已經(jīng)取了一個(gè)切片或不能再過(guò)濾的侧甫。
例如,刪除特定日志中的所有條目:
>>> b = Blog.objects.get(pk=1)
# Delete all the entries belonging to this Blog.
>>> Entry.objects.filter(blog=b).delete()
(4, {'weblog.Entry': 2, 'weblog.Entry_authors': 2})
默認(rèn)情況下蹋宦,Django的 ForeignKey
模擬SQL約束 ON DELETE CASCADE
-換言之披粟,任何具有指向要?jiǎng)h除對(duì)象的外鍵的對(duì)象都將與它們一起刪除。例如::
>>> blogs = Blog.objects.all()
# This will delete all Blogs and all of their Entry objects.
>>> blogs.delete()
(5, {'weblog.Blog': 1, 'weblog.Entry': 2, 'weblog.Entry_authors': 2})
這個(gè)級(jí)聯(lián)行為可以通過(guò) on_delete
論據(jù) ForeignKey
.
這個(gè) delete()
方法執(zhí)行批量刪除冷冗,不調(diào)用任何 delete()
模型上的方法守屉。但是,它確實(shí)會(huì)發(fā)出 pre_delete
和 post_delete
所有已刪除對(duì)象的信號(hào)(包括級(jí)聯(lián)刪除)蒿辙。
Django需要將對(duì)象提取到內(nèi)存中以發(fā)送信號(hào)并處理級(jí)聯(lián)拇泛。但是,如果沒(méi)有級(jí)聯(lián)和信號(hào)思灌,那么Django可能會(huì)采用快速路徑俺叭,刪除對(duì)象而不提取到內(nèi)存中。對(duì)于較大的刪除泰偿,這可能會(huì)導(dǎo)致內(nèi)存使用顯著減少熄守。執(zhí)行的查詢量也可以減少。
設(shè)置為 on_delete
DO_NOTHING
不要阻止刪除時(shí)采用快速路徑。
注意裕照,在對(duì)象刪除中生成的查詢是一個(gè)可更改的實(shí)現(xiàn)細(xì)節(jié)攒发。
as_manager()
-
classmethod
as_manager
()
返回的實(shí)例的類方法 Manager
一份 QuerySet
的方法。見(jiàn) 使用創(chuàng)建經(jīng)理 QuerySet 方法 了解更多詳細(xì)信息晋南。
explain()
-
explain
(format=None, **options)
返回 QuerySet
的執(zhí)行計(jì)劃惠猿,其中詳細(xì)說(shuō)明了數(shù)據(jù)庫(kù)將如何執(zhí)行查詢,包括將要使用的任何索引或聯(lián)接负间。了解這些詳細(xì)信息可能有助于提高慢速查詢的性能偶妖。
例如,使用PostgreSQL時(shí):
>>> print(Blog.objects.filter(title='My Blog').explain())
Seq Scan on blog (cost=0.00..35.50 rows=10 width=12)
Filter: (title = 'My Blog'::bpchar)
數(shù)據(jù)庫(kù)之間的輸出差異很大唉擂。
explain()
所有內(nèi)置數(shù)據(jù)庫(kù)后端都支持餐屎,但Oracle除外,因?yàn)閷?shí)現(xiàn)并不簡(jiǎn)單玩祟。
這個(gè) format
參數(shù)從數(shù)據(jù)庫(kù)的默認(rèn)值(通掣顾酰基于文本)更改輸出格式。PostgreSQL支持 'TEXT'
空扎, 'JSON'
藏鹊, 'YAML'
和 'XML'
. MySQL支持 'TEXT'
(也稱為 'TRADITIONAL'
) 'JSON'
.
一些數(shù)據(jù)庫(kù)接受可以返回有關(guān)查詢的更多信息的標(biāo)志。將這些標(biāo)志作為關(guān)鍵字參數(shù)傳遞转锈。例如盘寡,使用PostgreSQL時(shí):
>>> print(Blog.objects.filter(title='My Blog').explain(verbose=True))
Seq Scan on public.blog (cost=0.00..35.50 rows=10 width=12) (actual time=0.004..0.004 rows=10 loops=1)
Output: id, title
Filter: (blog.title = 'My Blog'::bpchar)
Planning time: 0.064 ms
Execution time: 0.058 ms
在某些數(shù)據(jù)庫(kù)上,標(biāo)記可能會(huì)導(dǎo)致執(zhí)行查詢撮慨,這可能會(huì)對(duì)數(shù)據(jù)庫(kù)產(chǎn)生不利影響竿痰。例如,PostgreSQL的 ANALYZE
如果存在觸發(fā)器或調(diào)用函數(shù)砌溺,則標(biāo)志可能導(dǎo)致對(duì)數(shù)據(jù)的更改影涉,即使對(duì)于 SELECT
查詢。
2.3.3 QuerySet惰性執(zhí)行和緩存
生成一個(gè)QuerySet
對(duì)象并不會(huì)馬上轉(zhuǎn)換為SQL
語(yǔ)句去執(zhí)行规伐。
比如我們獲取Book
表下所有的圖書(shū):
books = Book.objects.all()
print(connection.queries)
我們可以看到在打印connection.quries
的時(shí)候打印的是一個(gè)空的列表蟹倾。說(shuō)明上面的QuerySet
并沒(méi)有真正的執(zhí)行。
在以下情況下QuerySet
會(huì)被轉(zhuǎn)換為SQL
語(yǔ)句執(zhí)行:
-
迭代:在遍歷
QuerySet
對(duì)象的時(shí)候猖闪,會(huì)首先先執(zhí)行這個(gè)SQL
語(yǔ)句鲜棠,然后再把這個(gè)結(jié)果返回進(jìn)行迭代。比如以下代碼就會(huì)轉(zhuǎn)換為SQL
語(yǔ)句:for book in Book.objects.all(): print(book)
使用步長(zhǎng)做切片操作:
QuerySet
可以類似于列表一樣做切片操作培慌。做切片操作本身不會(huì)執(zhí)行SQL
語(yǔ)句豁陆,但是如果如果在做切片操作的時(shí)候提供了步長(zhǎng),那么就會(huì)立馬執(zhí)行SQL
語(yǔ)句吵护。需要注意的是盒音,做切片后不能再執(zhí)行filter
方法竖配,否則會(huì)報(bào)錯(cuò)。調(diào)用
len
函數(shù):調(diào)用len
函數(shù)用來(lái)獲取QuerySet
中總共有多少條數(shù)據(jù)也會(huì)執(zhí)行SQL
語(yǔ)句里逆。調(diào)用
list
函數(shù):調(diào)用list
函數(shù)用來(lái)將一個(gè)QuerySet
對(duì)象轉(zhuǎn)換為list
對(duì)象也會(huì)立馬執(zhí)行SQL
語(yǔ)句。判斷:如果對(duì)某個(gè)
QuerySet
進(jìn)行判斷用爪,也會(huì)立馬執(zhí)行SQL
語(yǔ)句原押。
2.4 刪除數(shù)據(jù)
在查找到數(shù)據(jù)后,便可以進(jìn)行刪除了偎血。刪除數(shù)據(jù)非常簡(jiǎn)單诸衔,只需要調(diào)用這個(gè)對(duì)象的delete
方法即可。實(shí)例代碼如下:
book = Book.objects.get(name='三國(guó)演義')
book.delete()
2.5 修改數(shù)據(jù)
在查找到數(shù)據(jù)后颇玷,便可以進(jìn)行修改了笨农。修改的方式非常簡(jiǎn)單,只需要將查找出來(lái)的對(duì)象的某個(gè)屬性進(jìn)行修改帖渠,然后再調(diào)用這個(gè)對(duì)象的save
方法便可以進(jìn)行修改谒亦。示例代碼如下:
from datetime import datetime
book = Book.objects.get(name='三國(guó)演義')
book.pub_date = datetime.now()
book.save()