Django中Q查詢及Q()對象


Django中Q查詢及Q()對象

問題

一般我們在Django程序中查詢數據庫操作都是在QuerySet里進行進行,例如下面代碼:

>>> q1 = Entry.objects.filter(headline__startswith="What")
>>> q2 = q1.exclude(pub_date__gte=datetime.date.today())
>>> q3 = q1.filter(pub_date__gte=datetime.date.today())
 

或者將其組合起來,例如:

>>>q1 = Entry.objects.filter(headline_startswith="What").exclude(pub_date_gte=datetime.date.today())
 

隨着我們的程序越來越復雜,查詢的條件也跟着復雜起來,這樣簡單的通過一個filter()來進行查詢的條件將導致我們的查詢越來越長。

Q()對象就是為了將這些條件組合起來。

當我們在查詢的條件中需要組合條件時(例如兩個條件“且”或者“或”)時。我們可以使用Q()查詢對象。例如下面的代碼

fromdjango.db.modelsimports Q
q=Q(question_startswith="What")
 

這樣就生成了一個Q()對象,我們可以使用符號&或者|將多個Q()對象組合起來傳遞給filter(),exclude(),get()等函數。當多個Q()對象組合起來時,Django會自動生成一個新的Q()。例如下面代碼就將兩個條件組合成了一個

Q(question__startswith='Who') | Q(question__startswith='What')
 

使用上述代碼可以使用SQL語句這么理解:

WHEREquestionLIKE 'Who%' ORquestionLIKE 'What%'
 

我們可以在Q()對象的前面使用字符“~”來代表意義“非”,例如下面代碼:

Q(question__startswith='Who') | ~Q(pub_date__year=2005)
 

對應SQL語句可以理解為:

WHEREquestionlike "Who%" ORyear(pub_date) !=2005
 

這樣我們可以使用 “&”或者“|”還有括號來對條件進行分組從而組合成更加復雜的查詢邏輯。

也可以傳遞多個Q()對象給查詢函數,例如下面代碼:

News.objects.get(
    Q(question__startswith='Who'),
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
)
 

多個Q()對象之間的關系Django會自動理解成“且(and)”關系。如上面代碼使用SQL語句理解將會是:

SELECT * fromnewsWHEREquestionLIKE 'Who%'  AND (pub_date = '2005-05-02' ORpub_date = '2005-05-06')
 

Q()對象可以結合關鍵字參數一起傳遞給查詢函數,不過需要注意的是要將Q()對象放在關鍵字參數的前面,看下面代碼

#正確的做法
News.objects.get(
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
    question__startswith='Who')
 
#錯誤的做法,代碼將關鍵字參數放在了Q()對象的前面。
News.objects.get(
    question__startswith='Who',
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)))
 

使用Q 對象進行復雜的查詢

filter() 等方法中的關鍵字參數查詢都是一起進行“AND” 的。 如果你需要執行更復雜的查詢(例如OR 語句),你可以使用Q 對象

Q 對象 (django.db.models.Q) 對象用於封裝一組關鍵字參數。這些關鍵字參數就是上文“字段查詢” 中所提及的那些。

例如,下面的Q 對象封裝一個LIKE 查詢:

from django.db.models import Q
Q(question__startswith='What')

Q 對象可以使用&| 操作符組合起來。當一個操作符在兩個Q 對象上使用時,它產生一個新的Q 對象。

例如,下面的語句產生一個Q 對象,表示兩個"question__startswith" 查詢的“OR” :

Q(question__startswith='Who') | Q(question__startswith='What')

它等同於下面的SQL WHERE 子句:

WHERE question LIKE 'Who%' OR question LIKE 'What%'

你可以組合&| 操作符以及使用括號進行分組來編寫任意復雜的Q 對象。同時,Q 對象可以使用~ 操作符取反,這允許組合正常的查詢和取反(NOT) 查詢:

Q(question__startswith='Who') | ~Q(pub_date__year=2005)

每個接受關鍵字參數的查詢函數(例如filter()exclude()get())都可以傳遞一個或多個Q 對象作為位置(不帶名的)參數。如果一個查詢函數有多個Q 對象參數,這些參數的邏輯關系為“AND"。例如:

Poll.objects.get(
    Q(question__startswith='Who'),
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
)

... 大體上可以翻譯成這個SQL:

SELECT * from polls WHERE question LIKE 'Who%'
    AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')

查詢函數可以混合使用Q 對象和關鍵字參數。所有提供給查詢函數的參數(關鍵字參數或Q 對象)都將"AND”在一起。但是,如果出現Q 對象,它必須位於所有關鍵字參數的前面。例如:

Poll.objects.get(
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
    question__startswith='Who')

... 是一個合法的查詢,等同於前面的例子;但是:

# INVALID QUERY
Poll.objects.get(
    question__startswith='Who',
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)))

... 是不合法的。

另見

Django 單元測試中的OR 查詢示例演示了幾種Q 的用法。

Django的Q對象實現的源碼中:

# 位於/django/db/models/query_utils.py  
  
class Q(tree.Node):  
    """ 
    Encapsulates filters as objects that can then be combined logically (using 
    & and |). 
    """  
    # Connection types  
    AND = 'AND'  
    OR = 'OR'  
    default = AND  
  
    def __init__(self, *args, **kwargs):  
        super(Q, self).__init__(children=list(args) + kwargs.items())  
  
    def _combine(self, other, conn):  
        if not isinstance(other, Q):  
            raise TypeError(other)  
        obj = type(self)()  
        obj.add(self, conn)  
        obj.add(other, conn)  
        return obj  
  
    def __or__(self, other):  
        return self._combine(other, self.OR)  
  
    def __and__(self, other):  
        return self._combine(other, self.AND)  
  
    def __invert__(self):  
        obj = type(self)()  
        obj.add(self, self.AND)  
        obj.negate()  
        return obj  

傳Q對象,構造搜索條件

首先還是需要導入模塊:

from django.db.models import Q

傳入條件進行查詢:

q1 = Q()
q1.connector = 'OR'
q1.children.append(('id', 1))
q1.children.append(('id', 2))
q1.children.append(('id', 3))
    
models.Tb1.objects.filter(q1)

合並條件進行查詢:

con = Q()

q1 = Q()
q1.connector = 'OR'
q1.children.append(('id', 1))
q1.children.append(('id', 2))
q1.children.append(('id', 3))

q2 = Q()
q2.connector = 'OR'
q2.children.append(('status', '在線'))

con.add(q1, 'AND')
con.add(q2, 'AND')

models.Tb1.objects.filter(con)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM