Django ORM 總的來講仍是很是好用的,可是坑仍是要一個一個踩。在使用 ORM 的同時使用 debug_toolbar 或者 logging 查看數據庫查詢語句,才能玩得溜啊。python
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 查詢到的兩列元素打印出來,但只作了一次查詢
關係型數據庫中常見的關係類型有 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)
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)