Short answer: If you do not pass a _connector yourself. Django will end up using 'AND' as a connector.
A Q object is a subclass of tree.Node. Indeed we can inspect the source code [GitHub]:
class Q(tree.Node):
"""
Encapsulate filters as objects that can then be combined logically (using
`&` and `|`).
"""
# Connection types
AND = 'AND'
OR = 'OR'
default = AND
conditional = True
def __init__(self, *args, _connector=None, _negated=False, **kwargs):
super().__init__(children=[*args, *sorted(kwargs.items())], connector=_connector, negated=_negated)
# …
When we thus construct a Q object, we make a super call to the tree.Node data constructor, and if we do not pass a _connector, it will take None as value.
The Node itself will however take the default in case the truthiness of the connector is None. Indeed, if we look at the source code of Node [GitHub], we see:
class Node:
"""
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):
"""Construct a new Node. If no connector is given, use the default."""
self.children = children[:] if children else []
self.connector = connector or self.default
self.negated = negated
# …
This thus means that if you construct a Q object, the default connector is the value of Q.AND and thus 'AND'.