Django模型(2)

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è)查詢只查詢到了idfirst_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í)例熬词, 查詢一般就是使用filterexclude以及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)用objectsfilter方法。實(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條件。下面是常用的查詢條件的使用:

  1. 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;

  1. 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的拥知。所以exactiexact的區(qū)別實(shí)際上就是 LIKE=的區(qū)別踏拜,在大部分collation=utf8_general_ci情況下都是一樣的(collation是用來(lái)對(duì)字符串比較的)。

  1. 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)的猜欺,意味著使用的是精確的查詢位隶。

  1. icontains:大小寫(xiě)不敏感的匹配查詢。示例代碼如下:
articles = Article.objects.filter(title__icontains='hello')

? 在翻譯成SQL語(yǔ)句為如下:

select ... where title like '%hello%';

  1. 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%');

  1. gt:某個(gè)field的值要大于給定的值。示例代碼如下:
articles = Article.objects.filter(id__gt=4)

? 以上代碼的意思是將所有id大于4的文章全部都找出來(lái)疚颊。
? 將翻譯成以下SQL語(yǔ)句:

select ... where id > 4;

  1. gte:類似于gt狈孔,是大于等于。

  2. lt:類似于gt是小于材义。

  3. lte:類似于lt均抽,是小于等于。

  4. 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%'

  1. istartswith:類似于startswith,但是大小寫(xiě)是不敏感的深寥。

  2. endswith:判斷某個(gè)字段的值是否以某個(gè)值結(jié)束攘乒。大小寫(xiě)敏感。示例代碼如下:

articles = Article.objects.filter(title__endswith='world')

? 以上代碼的意思是提取所有標(biāo)題以world結(jié)尾的文章惋鹅。
? 將翻譯成以下SQL語(yǔ)句:

select ... where title like '%world';

  1. iendswith:類似于endswith则酝,只不過(guò)大小寫(xiě)不敏感。

  2. 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/12018/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.datetimenavie時(shí)間轉(zhuǎn)換為aware時(shí)間刽沾。make_aware會(huì)將指定的時(shí)間轉(zhuǎn)換為TIME_ZONE中指定的時(shí)區(qū)的時(shí)間。

  1. 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中蛤售。

  1. 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';

  1. month:同year妒潭,根據(jù)月份進(jìn)行查找。

  2. day:同year鳄炉,根據(jù)日期進(jìn)行查找杜耙。

  3. week_day:同year,根據(jù)星期幾進(jìn)行查找拂盯。1表示星期天佑女,7表示星期六,2-6代表的是星期一到星期五谈竿。

  4. 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官方文檔 嚎花。

  1. isnull:根據(jù)值是否為空進(jìn)行查找。示例代碼如下:
articles = Article.objects.filter(pub_date__isnull=False)

以上的代碼的意思是獲取所有發(fā)布日期不為空的文章呀洲。
將來(lái)翻譯成SQL語(yǔ)句如下:

select ... where pub_date is not null;

  1. regexiregex:大小寫(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'

  1. 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}
    
    
  2. 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))
    
    
  3. MaxMin:獲取指定對(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}
    
    
  4. 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方法是只提取nametotal兩個(gè)字段的值。

  5. Variance:求指定對(duì)象的方差戴涝。

  6. 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á)式的值收厨。

比如如果想要獲取作者中,nameemail相同的作者數(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)的映砖。示例圖如下:

QuerySet和Manager.png

所以我們?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ì)象的方法三娩。

  1. filter:將滿足條件的數(shù)據(jù)提取出來(lái),返回一個(gè)新的QuerySet妹懒。具體的filter可以提供什么條件查詢尽棕。請(qǐng)見(jiàn)查詢操作章節(jié)。

  2. exclude:排除滿足條件的數(shù)據(jù)彬伦,返回一個(gè)新的QuerySet滔悉。示例代碼如下:

     Article.objects.exclude(title__contains='hello')
    
    

    以上代碼的意思是提取那些標(biāo)題不包含hello的圖書(shū)。

  3. 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è)文章的作者的年齡。

  4. 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í)間闺魏。

  5. 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è)惡模型中所有的屬性沮焕。

  6. 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',...]>
    
    
  7. all:獲取這個(gè)ORM模型的QuerySet對(duì)象谷遂。

  8. select_related:在提取某個(gè)模型的數(shù)據(jù)的同時(shí),也提前將相關(guān)聯(lián)的數(shù)據(jù)提取出來(lái)卖鲤。比如提取文章數(shù)據(jù)肾扰,可以使用select_relatedauthor信息提取出來(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)簽莲绰。

  9. 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ā)生兩次查詢操作疙渣。

  10. 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)玖媚。

  11. only:跟defer類似箕肃,只不過(guò)defer是過(guò)濾掉指定的字段,而only是只提取指定的字段今魔。

  12. get:獲取滿足條件的數(shù)據(jù)勺像。這個(gè)函數(shù)只能返回一條數(shù)據(jù),并且如果給的條件有多條數(shù)據(jù)错森,那么這個(gè)方法會(huì)拋出MultipleObjectsReturned錯(cuò)誤吟宦,如果給的條件沒(méi)有任何數(shù)據(jù),那么就會(huì)拋出DoesNotExit錯(cuò)誤涩维。所以這個(gè)方法在獲取數(shù)據(jù)的只能殃姓,只能有且只有一條。

  13. 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')
    
    
  14. 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)建的。

  15. bulk_create:一次性創(chuàng)建多個(gè)數(shù)據(jù)。示例代碼如下:

    Tag.objects.bulk_create([
        Tag(name='111'),
        Tag(name='222'),
    ])
    
    
  16. count:獲取提取的數(shù)據(jù)的個(gè)數(shù)须妻。如果想要知道總共有多少條數(shù)據(jù)仔蝌,那么建議使用count,而不是使用len(articles)這種荒吏。因?yàn)?code>count在底層是使用select count(*)來(lái)實(shí)現(xiàn)的敛惊,這種方式比使用len函數(shù)更加的高效。

  17. firstlast:返回QuerySet中的第一條和最后一條數(shù)據(jù)绰更。

  18. aggregate:使用聚合函數(shù)瞧挤。

  19. 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)
    
    
  20. 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)瘦赫。

  21. update:執(zhí)行更新操作,在SQL底層走的也是update命令蛤迎。比如要將所有category為空的articlearticle字段都更新為默認(rèn)的分類确虱。示例代碼如下:

    Article.objects.filter(category__isnull=True).update(category_id=3)
    
    

    注意這個(gè)方法走的是更新的邏輯。所以更新完成后保存到數(shù)據(jù)庫(kù)中不會(huì)執(zhí)行save方法替裆,因此不會(huì)更新auto_now設(shè)置的字段校辩。

  22. delete:刪除所有滿足條件的數(shù)據(jù)。刪除數(shù)據(jù)的時(shí)候辆童,要注意on_delete指定的處理方式宜咒。

  23. 切片操作:有時(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ù)層面使用LIMIEOFFSET來(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)) uniqueunique_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_savepost_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_savepost_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_listfield_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_datecomments_on

您可以更新多個(gè)字段-數(shù)量沒(méi)有限制馏慨。例如埂淮,這里我們更新 comments_onheadline 領(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_savepost_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_deletepost_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í)行:

  1. 迭代:在遍歷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)
    
    
  2. 使用步長(zhǎng)做切片操作:QuerySet可以類似于列表一樣做切片操作培慌。做切片操作本身不會(huì)執(zhí)行SQL語(yǔ)句豁陆,但是如果如果在做切片操作的時(shí)候提供了步長(zhǎng),那么就會(huì)立馬執(zhí)行SQL語(yǔ)句吵护。需要注意的是盒音,做切片后不能再執(zhí)行filter方法竖配,否則會(huì)報(bào)錯(cuò)。

  3. 調(diào)用len函數(shù):調(diào)用len函數(shù)用來(lái)獲取QuerySet中總共有多少條數(shù)據(jù)也會(huì)執(zhí)行SQL語(yǔ)句里逆。

  4. 調(diào)用list函數(shù):調(diào)用list函數(shù)用來(lái)將一個(gè)QuerySet對(duì)象轉(zhuǎn)換為list對(duì)象也會(huì)立馬執(zhí)行SQL語(yǔ)句。

  5. 判斷:如果對(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()

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市空郊,隨后出現(xiàn)的幾起案子份招,更是在濱河造成了極大的恐慌,老刑警劉巖狞甚,帶你破解...
    沈念sama閱讀 219,539評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件锁摔,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡哼审,警方通過(guò)查閱死者的電腦和手機(jī)谐腰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)涩盾,“玉大人十气,你說(shuō)我怎么就攤上這事∨陨蓿” “怎么了桦踊?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,871評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)终畅。 經(jīng)常有香客問(wèn)我籍胯,道長(zhǎng),這世上最難降的妖魔是什么离福? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,963評(píng)論 1 295
  • 正文 為了忘掉前任杖狼,我火速辦了婚禮,結(jié)果婚禮上妖爷,老公的妹妹穿的比我還像新娘蝶涩。我一直安慰自己理朋,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著邑飒,像睡著了一般冒黑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上兽愤,一...
    開(kāi)封第一講書(shū)人閱讀 51,763評(píng)論 1 307
  • 那天,我揣著相機(jī)與錄音挪圾,去河邊找鬼浅萧。 笑死,一個(gè)胖子當(dāng)著我的面吹牛哲思,可吹牛的內(nèi)容都是我干的洼畅。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼棚赔,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼帝簇!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起忆嗜,我...
    開(kāi)封第一講書(shū)人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤己儒,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后捆毫,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體闪湾,經(jīng)...
    沈念sama閱讀 45,850評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評(píng)論 3 338
  • 正文 我和宋清朗相戀三年绩卤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了途样。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,144評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡濒憋,死狀恐怖何暇,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情凛驮,我是刑警寧澤裆站,帶...
    沈念sama閱讀 35,823評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站黔夭,受9級(jí)特大地震影響宏胯,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜本姥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評(píng)論 3 331
  • 文/蒙蒙 一肩袍、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧婚惫,春花似錦氛赐、人聲如沸魂爪。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,026評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)滓侍。三九已至,卻和暖如春牲芋,著一層夾襖步出監(jiān)牢的瞬間粗井,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,150評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工街图, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人懒构。 一個(gè)月前我還...
    沈念sama閱讀 48,415評(píng)論 3 373
  • 正文 我出身青樓餐济,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親胆剧。 傳聞我的和親對(duì)象是個(gè)殘疾皇子絮姆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容