84.經常使用的返回QuerySet對象的方法使用詳解:select_related, prefetch_related

好比,想要獲取與Book表經過外鍵的形式相關聯的Author表中的數據,示例代碼以下:
from django.db import connection
from django.http import HttpResponse
from .models import Article, Book, BookOrder
def index4(request):
    # 1. select_related()想要獲取與Book表經過外鍵的形式相關聯的Author表中的數據
    # 能夠經過select_related()將相關聯的表中的數據提取到內存中,減小到數據庫中的查詢
    books = Book.objects.select_related('author')
    for book in books:
        print("%s, %s" % (book.author.name, book.author.age))
    print(connection.queries) 
    # 最後django底層能夠執行一條sql語句就將全部的查詢操做完成

django底層執行的sql語句爲:python

[{'sql': 'SELECT @@SQL_AUTO_IS_NULL', 'time': '0.000'},
{'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED', 'time': '0.000'},
{'sql': 'SELECT book.id, book.name, book.pages, book.price, book.rating, book.author_id, book.publisher_id, book.score, author.id, author.name, author.age, author.email FROM book INNER JOIN author ON (book.author_id = author.id)', 'time': '0.016'}]sql

由以上的sql語句,能夠看出只進行了一次查詢操做,因此這就大大提升了查詢的效率了。所以若是在數據量不是太大,而且常常會使用到該表的相關聯的表中的數據時,就能夠採用select_related()方法首先將數據提取到內存中,以便以後在進行查找的時候能夠減小向數據庫中發起請求的次數。

2. 採用傳統的方式進行查找,獲取Book表經過外鍵的形式相關聯的Author表中的數據,

示例代碼以下:
books = Book.objects.all()
    # 採用all()方法獲取每一個book的信息,而後向數據庫中提出查詢操做每一個book進行一次數據庫的查詢,提取出name,age字段的值
    for book in books:
        print("%s,%s" % (book.author.name, book.author.age))
    print(connection.queries)

django底層執行的sql語句爲:
[{'sql': 'SELECT @@SQL_AUTO_IS_NULL', 'time': '0.000'},
{'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED', 'time': '0.000'},
{'sql': 'SELECT book.id, book.name, book.pages, book.price, book.rating, book.author_id, book.publisher_id, book.score FROM book', 'time': '0.000'},
{'sql': 'SELECT author.id, author.name, author.age, author.email FROM author WHERE author.id = 5 LIMIT 21', 'time': '0.000'},
{'sql': 'SELECT author.id, author.name, author.age, author.email FROM author WHERE author.id = 1 LIMIT 21', 'time': '0.000'},
{'sql': 'SELECT author.id, author.name, author.age, author.email FROM author WHERE author.id = 4 LIMIT 21', 'time': '0.000'},
{'sql': 'SELECT author.id, author.name, author.age, author.email FROM author WHERE author.id = 3 LIMIT 21', 'time': '0.000'}]數據庫

由以上sql語句能夠看出,採用傳統的方式執行的sql語句較多,這會大大的下降查詢的效率。因此若是數據量不是太大的狀況下能夠採用select_related()方法將數據提取到內存中。
好比,求每本書的訂閱量。示例代碼以下:
def index(request):
 # 3. prefetch_related()能夠對多對多,多對一的關聯模型操做
    # 好比,求每一個書的訂閱量
    # 返回的books中的是包裹在QuerySet中的book對象
    books = Book.objects.prefetch_related('bookorder_set')
    print(type(books))
    # <class 'django.db.models.query.QuerySet'>
    for book in books:
        print(type(book))
    # <class 'front.models.Book'>, 以後就能夠對返回的每一本書上的屬性進行操做
    #   如下咱們能夠提取出每本書上的全部訂單信息
    # 注意:若是已經使用prefetch_related()進行了相關的查找,這裏就不要使用filter()或者是其餘的能夠返回QuerySet的任何操做了。
    # book_nums = book.bookorder_set.filter(price__gte=80)
    # 這裏可使用all()方法進行獲取。若是對bookorder再次進行了查詢就會增長n個查詢語句,下降查詢的效率
        book_nums = book.bookorder_set.all()
        for book_num in book_nums:
            print("%s, %s, %s" % (book_num.book.name, book_num.price, book_num.id))
    print(connection.queries)
    return HttpResponse("success!")

django底層執行的sql語句爲:django

[{'sql': 'SELECT @@SQL_AUTO_IS_NULL', 'time': '0.000'},
{'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED', 'time': '0.000'},
{'sql': 'SELECT book.id, book.name, book.pages, book.price, book.rating, book.author_id, book.publisher_id, book.score FROM book', 'time': '0.000'},
{'sql': 'SELECT book_order.id, book_order.book_id, book_order.price, book_order.time FROM book_order WHERE book_order.book_id IN (1, 2, 3, 4)', 'time': '0.000'}]函數

總結:在多對多,多對一關聯對象中執行查詢操做,可使用prefetch_related()進行相關查詢,這種方式中能夠大大減小執行的sql語句,而且這種方式會產生兩條查詢語句。一樣也可使用prefetch_related()對一對多或者是一對一的關聯對象進行相關查詢,一樣也會在底層執行兩條sql語句,若是使用select_related()進行查詢的話只會執行一條查詢語句。因此說若是是對一對一或者是一對多關聯對象執行操做的話,建議使用select_related(); 若是是多對一或者是多對多關聯模型執行操做時,就能夠採用prefetch_related()fetch

4. 採用傳統的方式執行與prefetch_related()做用相同的操做,查看django底層執行的sql語句,

示例代碼以下:
def index(request):
# 4. 使用傳統的方法對多對多或者是多對一的關聯模型表進行查詢操做
    books = Book.objects.all()
    for book in books:
        print(type(book))
    #     # <class 'front.models.Book'>
        book_nums = book.bookorder_set.all()
        for book_num in book_nums:
            print(type(book_num))
    #         # <class 'front.models.BookOrder'>
            print("%s, %s, %s" % (book_num.book.name, book_num.price, book_num.id))
    print(connection.queries)
    return HttpResponse("success")

django底層執行的sql語句:
[{'sql': 'SELECT @@SQL_AUTO_IS_NULL', 'time': '0.000'},
{'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED', 'time': '0.000'},
{'sql': 'SELECT book.id, book.name, book.pages, book.price, book.rating, book.author_id, book.publisher_id, book.score FROM book', 'time': '0.000'},
{'sql': 'SELECT book_order.id, book_order.book_id, book_order.price, book_order.time FROM book_order WHERE book_order.book_id = 1', 'time': '0.000'},
{'sql': 'SELECT book_order.id, book_order.book_id, book_order.price, book_order.time FROM book_order WHERE book_order.book_id = 2', 'time': '0.000'},
{'sql': 'SELECT book_order.id, book_order.book_id, book_order.price, book_order.time FROM book_order WHERE book_order.book_id = 3', 'time': '0.000'},
{'sql': 'SELECT book_order.id, book_order.book_id, book_order.price, book_order.time FROM book_order WHERE book_order.book_id = 4', 'time': '0.016'}]code

由以上所執行的sql語句能夠看出,採用傳統的方式會使底層執行的sql語句的數量增大

5. 若是在某些狀況下須要對預先查詢的結果,再進行一些操做,可使用Prefetch()函數,

示例代碼以下:
def index5(request):
    # 5.若是確實要對預先查詢的結果,再進行一些操做的話,可使用Prefetch()函數
    # 能夠將要進行的操做賦值給Prefetch函數中的queryset參數
    prefetch = Prefetch('bookorder_set', queryset=BookOrder.objects.filter(price__gte=90))
    books = Book.objects.prefetch_related(prefetch)
    for book in books:
            # <class 'front.models.BookOrder'>
        orders = book.bookorder_set.all()
        for order in orders:
            print("%s, %s, %s" % (order.book.name, order.price, order.id))
    print(connection.queries)
    return HttpResponse("success")

django底層執行的sql語句爲:
水滸傳, 95.0, 2
[{'sql': 'SELECT @@SQL_AUTO_IS_NULL', 'time': '0.000'},
{'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED', 'time': '0.000'},
{'sql': 'SELECT book.id, book.name, book.pages, book.price, book.rating, book.author_id, book.publisher_id, book.score FROM book', 'time': '0.000'},
{'sql': 'SELECT book_order.id, book_order.book_id, book_order.price, book_order.time FROM book_order WHERE (book_order.price >= 90.0e0 AND book_order.book_id IN (1, 2, 3, 4))', 'time': '0.000'}]對象

相關文章
相關標籤/搜索