Django ORM 入坑記錄

前言

Django ORM 總的來講仍是很是好用的,可是坑仍是要一個一個踩。在使用 ORM 的同時使用 debug_toolbar 或者 logging 查看數據庫查詢語句,才能玩得溜啊。python

Queryset 緩存

Django 的 查詢是帶有 Lazy 的,並且是有緩存的。Lazy 就在於查詢只會在查詢結果被調用時纔開始進行,查詢結果會保存在緩存中,屢次訪問同一查詢結果裏的內容不會產生新的查詢。而須要注意的以下:數據庫

bids = Bidding.objects.all()
# 我發現以下打印查詢結果,分別會進行進行兩次數據庫查詢,一共進行了兩次查詢操做
bids[0]
bids[1]

# 可是若是咱們先將 bids 轉化爲 list 類型,或者直接將 bids 作成 for 循環,以下:
bidlist = list(bids)
bidlist[0]
bidlist[1]
# 或者
for bid in bids:
    print bid.id
# 二者都會將 bids 查詢到的兩列元素打印出來,但只作了一次查詢

prefetch_related 和 select_related

關係型數據庫中常見的關係類型有 OneToOne, ManyToOne, ManyToMany。公司員工表中的一個員工與其在另一張員工信息表中的關係爲 OneToOne,這個員工在職位表中可能存在多個職位數據,這裏關係就是 ManyToOne 的了,而 ManyToMany 則能夠是衛生值日表,一我的能夠同時擦窗戶和擦桌子,同時掃地這件事情多是由多我的來作的。在 ManyToMany 的關係中,Django 會自動生成一張額外的表存儲對應信息。django

select_related 適用於 ManyToOne 和 OneToOne,用於存在外鍵時,採用 Join 操做事先提取全部外鍵對應的信息,避免了 n+1 次查詢。緩存

prefetch_related 適用於 ManyToOne 和 ManyToMany,不用一條 SQL 語句解決問題,而是稍微進行屢次查詢,採用 IN 操做減小查詢次數。詳情可見參考資料。函數

prefetch_related 和 select_related 默認是不支持分片的,以下:fetch

# #1
books = Book.objects.filter(writer__id=writer_id).prefetch_related('user', 'city', 'supplier', 'supplier__user')[start:end]
# #2
books = Book.objects.filter(writer__id=writer_id).select_related('user', 'city', 'supplier', 'supplier__user')[start:end]
# #3
books = Book.objects.filter(writer__id=writer_id)[start:end].prefetch_related('user', 'city', 'supplier', 'supplier__user')
# #4
books = Book.objects.filter(writer__id=writer_id)[start:end].select_related('user', 'city', 'supplier', 'supplier__user')

writers = Writer.objects.filter(books__in=books)

上述四種查詢都會讓 writer 這句代碼引發 This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery 的錯誤,緣由在於全部 books 的賦值語句都不會執行,而是因爲 lazy queryset 的緣由,這句查詢只會做爲一個子查詢帶入到 Writer 的查詢語句裏面,而 books 的子查詢語句中自帶了 limit,正巧子查詢不支持 limit,因此就報了錯。優化

要解決這個問題,能夠先將 books 變成一個現成的結果,好比將其變成一個 book_id 的 list,而後就能夠經過如下代碼來取出想要的結果啦。debug

writers = Writer.objects.filter(books_id__in=book_id_list)

更新 update 操做

Book.objects.filter(writer__id=writer_id).update(status=Book.OUT_OF_DATE)

會產生兩條數據庫語句,一條查詢,一條更新code

Book.objects.filter(id=book_id).update(status=Book.OUT_OF_DATE)

直接產生一條更新語句,緣由是前者的查詢條件中有外鍵。blog

不一樣類型的條件取並集

不一樣類型的條件取並集,在帶入查詢時候比較蛋疼,這裏用了一種比較醜陋的實現。

level_type = request.GET.get('level') or None
pay_status = request.GET.get('pay') or None
clearing_status = request.GET.get('clearing') or None
filters = Q()
if level_type:
    filters &= Q(level_type=level_type)
if pay_status:
    filters &= Q(pay_status=pay_status)
if clearing_status:
    filters &= Q(clearing_status=clearing_status)
orders = Order.objects.filter(filters)

參考資料

相關文章
相關標籤/搜索