模型.objects:
這個對象是django.db.models.manager.Manager的對象,這個類是一個空殼類,他上面的所有方法都是從QuerySet這個類上面拷貝過來的。因此我們只要學(xué)會了QuerySet,這個objects也就知道該如何使用了。
Manager源碼解析:
class_name = "BaseManagerFromQuerySet"
class_dict = {
'_queryset_class': QuerySet
}
class_dict.update(cls._get_queryset_methods(QuerySet))
# type動態(tài)的時候創(chuàng)建類
# 第一個參數(shù)是用來指定創(chuàng)建的類的名字。創(chuàng)建的類名是:BaseManagerFromQuerySet
# 第二個參數(shù)是用來指定這個類的父類。
# 第三個參數(shù)是用來指定這個類的一些屬性和方法
return type(class_name,(cls,),class_dict)
_get_queryset_methods:這個方法就是將QuerySet中的一些方法拷貝出來
filter/exclude/annotate:過濾/排除滿足條件的/給模型添加新的字段。
order_by:
# 根據(jù)創(chuàng)建的時間正序排序
articles = Article.objects.order_by("create_time")
# 根據(jù)創(chuàng)建的時間倒序排序
articles = Article.objects.order_by("-create_time")
# 根據(jù)作者的名字進(jìn)行排序
articles = Article.objects.order_by("author__name")
# 首先根據(jù)創(chuàng)建的時間進(jìn)行排序,如果時間相同,則根據(jù)作者的名字進(jìn)行排序
articles = Article.objects.order_by("create_time",'author__name')
一定要注意的一點是,多個order_by,會把前面排序的規(guī)則給打亂,而使用后面的排序方式。比如以下代碼:
articles = Article.objects.order_by("create_time").order_by("author__name")
他會根據(jù)作者的名字進(jìn)行排序,而不是使用文章的創(chuàng)建時間。
當(dāng)然,也可以在模型定義的在Meta類中定義ordering來指定默認(rèn)的排序方式。示例代碼如下:
class Meta:
db_table = 'book_order'
ordering = ['create_time','-price']
還可以根據(jù)annotate定義的字段進(jìn)行排序。比如要實現(xiàn)圖書的銷量進(jìn)行排序,那么示例代碼如下:
books = Book.objects.annotate(order_nums=Count("bookorder")).order_by("-order_nums")
for book in books:
print('%s/%s'%(book.name,book.order_nums))
values:
有時候我們在表中查找數(shù)據(jù)的時候,并不是想把所有的字段都提取出來。我們有可能只想要其中的幾個字段,這時候就可以使用values來實現(xiàn),需要幾個字段,就把這幾個字段的名字傳遞到這個方法中,示例代碼如下:
books = Book.objects.values('id', 'name')
values的返回值同樣是一個QuerySet對象,但其中不在是模型,而是字典。
如果我們想要提取的是這個模型上關(guān)聯(lián)的對象屬性,那么也是可以的,查找順序跟filter的用法是一樣的,示例代碼如下:
books = Book.objects.values('id', 'name', 'author__name')
以上將會提取author的name字段,如果我們不想使用這個名字。那么可以使用關(guān)鍵字 參數(shù),示例代碼如下:
books = Book.objects.values("id", "name", "author_name=F('author__name')")
注意:自定義的名字,不能和模型本身擁有的字段一樣,比如以上author_name取名author就會報錯,因為Book上本身就有author字段名字。
在values中,也可以使用聚合函數(shù)來形成一個新的字段,比如我想要獲取每本圖書的銷量,那么示例代碼如下:
books = Book.objects.values('id', 'name', order_nums=Count('bookorder'))
如果調(diào)用values方法時,沒有傳遞任何參數(shù),那么會獲取這個模型上的所有的字段以及對應(yīng)的值形成的字典,示例代碼如下:
books = Book.objects.values()
那么books中的值如下:
{'id': 1, 'name': '三國演義', 'pages': 987, 'price': 108.0, 'rating': 3.9, 'author_id': 3, 'publisher_id': 1}
values_list
跟values是一樣的作用,只不過這個方法返回的QuerySet中,裝的不是字典,而是元組,示例代碼如下:
books = Book.objects.values_list('id', 'name')
那么以上代碼返回的結(jié)果時:
(1, '三國i演義')
如果給values_list只指定一個字段,那我們可以指定flat=True,這樣返回回來的結(jié)果就不在是一個元組,而是這個字段的值,示例代碼如下:
books = Book.objects.values_list('name', flat=True)
那么以上返回的結(jié)果是:
'三國演義'
一點要注意的是,flat只能用在一個字段的情況下,否則就會報錯。
all:
這個方法簡單的返回一個QuerySet對象,這個QuerySet對象沒有經(jīng)過任何的修改(比如:過濾等)
select_related:
在查找某個表的數(shù)據(jù)的時候,可以一次性把相關(guān)聯(lián)的其他表的數(shù)據(jù)都提取出來,這樣可以在以后訪問相關(guān)聯(lián)的表的數(shù)據(jù)的時候,不用再次查詢數(shù)據(jù)庫,可以節(jié)省一些開銷。示例代碼如下:
books = Book.objects.select_related("author", ''publisher")
for book in books:
print(book.author.name)
# 因為在提取book時后,使用了select_related,
# 那么以后再訪問book.author的時候,不會再次項數(shù)據(jù)庫重新發(fā)起查詢
注意:這個方法只能用在外鍵關(guān)聯(lián)的對象上,對于那種多對多、或者多對一的情況,不能使用它來實現(xiàn),而應(yīng)該使用prefetch_related來實現(xiàn)。
prefetch_related:
這個方法類似于select_related方法,也是用來在查詢語句的時候,提前將指定的數(shù)據(jù)找出來,只不過這個方法用來解決多對多,或者多對一的情況,這個方法產(chǎn)生兩個查詢語句。所以,若果在這個方法中查詢使用外鍵關(guān)聯(lián)的模型時,也會產(chǎn)生兩個查詢語句,因此如果查詢的是外鍵關(guān)聯(lián)的模型建議使用select_related方法。在查詢多對多或者多對一的關(guān)聯(lián)對象的時候,在使用模型怎么訪問這個多對多,那么就在這個方法中傳遞什么字符串,比如要獲取圖書上的所有訂單,那么示例代碼如下:
books = Book.objects.preftch_related(''bookorder_set")
需要注意的是:在使用preftch_related查找出來的bookorder_ser,建議不要再對他進(jìn)行任何操作,不如filter,不然又會產(chǎn)生N多查詢語句,比如以下代碼是不對的:
books = Book.objects.preftch_related(''bookorder_set")
for book in books:
print('='*30)
print(book.name)
orders = book.bookorder_set.filter(price__gte=90)
for order in orders:
print(order.id)
那么如果確實像要對預(yù)先查找的集合進(jìn)行操作,那么我們可以使用django.db.models.Prefetch來完成,示例代碼如下:
form django.db.models import Prefetch
# 先使用Prefetch把查找的條件寫好,再放到prefetch_related中
prefetch = Prefetch("bookorder_set", queryset=BookOrder.objects.filter(price__gte=90))
books = Book.objects.preftch_related(prefetch)
for book in books:
print('='*30)
print(book.name)
orders = book.bookorder_set.all()
for order in orders:
print(order.id)
defer和only:
這個方法都會返回一個QuerySet對象,并且這個QuerySet中裝的都是模型,而不是字典。
1、defer: 這個方法用來告訴ORM, 在查詢某個模型的時候,過濾掉某些字段。
2、only: 這個方法用來告訴ORM,在查詢某個模型的時候,只提取某幾個字段。
注意:使用defer或者only,如果沒有提取這個字段,那么后面使用這個字段,會重新發(fā)起一次請求,因此要謹(jǐn)慎使用。
get:
獲取滿足條件的值,這個方法給定的條件只能匹配到一條數(shù)據(jù),如果匹配到多條數(shù)據(jù)或者沒有數(shù)據(jù),它都會報錯,因此使用時,都是根據(jù)主鍵為條件來匹配的。
create:
可以創(chuàng)建一條數(shù)據(jù),并且將這條數(shù)據(jù)保存到數(shù)據(jù)庫中,以下代碼是等價的。
publisher = Publisher(name='xxxx出版社')
publisher.save()
# 等價于下面這句
Publisher.objects.create(name='xxxx出版社')
get_or_create:
如果給定的條件有數(shù)據(jù),那么就會把這個數(shù)據(jù)給提取出來,如果給定的條件沒有數(shù)據(jù),就會先創(chuàng)建數(shù)據(jù),然后把數(shù)據(jù)給返回回來。
bulk_creat:
一次性創(chuàng)建多個數(shù)據(jù),示例代碼如下:
publisher = Publisher.objects.bulk_create([
Publisher(name='123出版社'),
Publisher(name='456出版社')
])
count:
獲取提取的數(shù)據(jù)個數(shù),如果想要知道總共有多少條數(shù)據(jù),那么建議使用count,而不是使用len(books)這種,因為Count在底層是使用select count(*)來實現(xiàn)的,這種方式比使用len函數(shù)更加高效。
first 和 last
返回QuerySet中第一和最后一條數(shù)據(jù)
exists:
判斷某個條件的數(shù)據(jù)是否存在,如果判斷某個條件的元素是否存在,建議使用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ù),這個方法如果底層數(shù)據(jù)庫使用的是MySQL,那么不能傳遞任何參數(shù),比如想要提取所有銷售價格超過80元的圖書,并且刪除掉那些重復(fù)的,那么可以使用distinct來幫我們實現(xiàn),示例代碼如下:
books = Book.objects.filter(bookorder__price__gte=80).distinct()
需要注意的是,如果distinct之前使用了order_by,那么order_by會提取order_by中指定的字段,因此,再使用distinct1就會根據(jù)多個字段來進(jìn)行唯一化,就不會把那些重復(fù)的數(shù)據(jù)刪除掉,示例代碼如下:
orders = BookOrder.objects.order_by('create_time').values('book_id').distinct()
# 那么以上代碼因為使用了order_by,即使使用了distinct,也會把重復(fù)的book_id提取出來
update和delete:
一次性可以把所有的數(shù)據(jù)都更新,
一次性可以把滿足條件的數(shù)據(jù)都刪除掉,但需要注意的刪除數(shù)據(jù)的時候,要注意on_delete指定的處理方式。
切片操作:
有時候我們查找數(shù)據(jù),有可能只需要其中的一部分,那么這時候可以使用切片操作來幫助我們完成。QuerySet使用切片操作就跟列表使用切片操作一樣,示例代碼如下:
books = Book.objects.all()[1:3]
for book in books:
print(book)
切片操作并不是把所有的數(shù)據(jù)從數(shù)據(jù)庫中提取出來再做切片操作,而是在數(shù)據(jù)庫層面使用LIMIT和OFFSET來幫助我們完成。
什么時候 Django 會將 QuerySet 轉(zhuǎn)換為 SQL 去執(zhí)行:
1、迭代
2、使用步長做切片操作
3、調(diào)用len函數(shù)
4、調(diào)用list函數(shù):將一個QuerySet對象轉(zhuǎn)換為list對象也會立即執(zhí)行SQL語句。
5、判斷:如果對某個QuerySet進(jìn)行片段,也會立馬執(zhí)行 SQL 語句。