filter 等方法中的關鍵字參數查詢都是一起進行“AND” 的。 如果你需要執行更復雜的查詢(例如OR 語句),你可以使用Q對象
調用Q
from django.db.models import Q
Q 對象可以使用c&(AND)和|(OR)操作符組合起來。
當一個操作符在兩個Q 對象上使用時,它產生一個新的Q 對象。
Q(question__startswith='Who') | Q(question__startswith='What')
等同於SQL語句
WHERE question LIKE 'Who%' OR question LIKE 'What%'
如果一個查詢函數有多個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 對象可以使用~ 操作符取反,這允許組合正常的查詢和取反(NOT) 查詢:
Q(question__startswith='Who') | ~Q(pub_date__year=2005)
查詢函數可以混合使用Q對象和關鍵字參數。所有提供給查詢函數的參數(關鍵字參數或Q 對象)都將"AND”在一起。但是,如果出現Q 對象,它必須位於所有關鍵字參數的前面。例如
Poll.objects.get(
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
question__startswith='Who')
#下列是不合法的查詢條件
Poll.objects.get( question__startswith='Who', Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)))
Q對象實例化使用:
# 獲取搜索條件
condition_dict = json.loads(request.GET.get('condition'))
'''
{'sn__contains': ['1', '2', '3', '1'], 'hostname__contains': ['c1', 'c2', 'c3']}
'''
from django.db.models import Q
#產生第一個Q對象
con = Q()
for k,v in condition_dict.items():
#產生第二個Q對象
temp = Q()
temp.connector = 'OR' # Connection types AND = 'AND' OR = 'OR' default = AND
for item in v:
temp.children.append((k,item)) #children是Q父類Node的屬性,默認是個列表
con.add(temp,'AND') # add 是父類Node的方法,
print(con)
'''
(AND: (OR: ('sn__contains', '1'), ('sn__contains', '1')), ('hostname__contains', 'c3'))
'''
models.Server.objects.filter(con)

#!/usr/bin/env python # -*- coding: utf-8 -*- # Created by Mona on 2017/10/24 import copy from django.utils.encoding import force_str, force_text class Node(object): """ A single internal node in the tree graph. A Node should be viewed as a connection (the root) with the children being either leaf nodes or other Node instances. """ # Standard connector type. Clients usually won't use this at all and # subclasses will usually override the value. default = 'DEFAULT' def __init__(self, children=None, connector=None, negated=False): """ Constructs a new Node. If no connector is given, the default will be used. """ self.children = children[:] if children else [] self.connector = connector or self.default self.negated = negated # We need this because of django.db.models.query_utils.Q. Q. __init__() is # problematic, but it is a natural Node subclass in all other respects. @classmethod def _new_instance(cls, children=None, connector=None, negated=False): """ This is called to create a new instance of this class when we need new Nodes (or subclasses) in the internal code in this class. Normally, it just shadows __init__(). However, subclasses with an __init__ signature that is not an extension of Node.__init__ might need to implement this method to allow a Node to create a new instance of them (if they have any extra setting up to do). """ obj = Node(children, connector, negated) obj.__class__ = cls return obj def __str__(self): template = '(NOT (%s: %s))' if self.negated else '(%s: %s)' return force_str(template % (self.connector, ', '.join(force_text(c) for c in self.children))) def __repr__(self): return str("<%s: %s>") % (self.__class__.__name__, self) def __deepcopy__(self, memodict): """ Utility method used by copy.deepcopy(). """ obj = Node(connector=self.connector, negated=self.negated) obj.__class__ = self.__class__ obj.children = copy.deepcopy(self.children, memodict) return obj def __len__(self): """ The size of a node if the number of children it has. """ return len(self.children) def __bool__(self): """ For truth value testing. """ return bool(self.children) def __nonzero__(self): # Python 2 compatibility return type(self).__bool__(self) def __contains__(self, other): """ Returns True is 'other' is a direct child of this instance. """ return other in self.children def add(self, data, conn_type, squash=True): """ Combines this tree and the data represented by data using the connector conn_type. The combine is done by squashing the node other away if possible. This tree (self) will never be pushed to a child node of the combined tree, nor will the connector or negated properties change. The function returns a node which can be used in place of data regardless if the node other got squashed or not. If `squash` is False the data is prepared and added as a child to this tree without further logic. """ if data in self.children: return data if not squash: self.children.append(data) return data if self.connector == conn_type: # We can reuse self.children to append or squash the node other. if (isinstance(data, Node) and not data.negated and (data.connector == conn_type or len(data) == 1)): # We can squash the other node's children directly into this # node. We are just doing (AB)(CD) == (ABCD) here, with the # addition that if the length of the other node is 1 the # connector doesn't matter. However, for the len(self) == 1 # case we don't want to do the squashing, as it would alter # self.connector. self.children.extend(data.children) return self else: # We could use perhaps additional logic here to see if some # children could be used for pushdown here. self.children.append(data) return data else: obj = self._new_instance(self.children, self.connector, self.negated) self.connector = conn_type self.children = [obj, data] return data def negate(self): """ Negate the sense of the root connector. """ self.negated = not self.negated class Q(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) + list(kwargs.items())) def _combine(self, other, conn): if not isinstance(other, Q): raise TypeError(other) obj = type(self)() obj.connector = conn 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 def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False): # We must promote any new joins to left outer joins so that when Q is # used as an expression, rows aren't filtered due to joins. clause, joins = query._add_q(self, reuse, allow_joins=allow_joins, split_subq=False) query.promote_joins(joins) return clause