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(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對象實現的源碼中:
[python] view plain copy
- # 位於/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)
Django中Q查詢及Q()對象