Описание
Метод filter()
в Django предназначен для фильтрации запросов на основе атрибутов модели или их комбинаций, при этом они указываются через запятую, а в SQL запросе будут соединены между собой логическим оператором AND
.
Использование же других логических операторов, таких как OR
, NOT
и т.д., напрямую для атрибутов модели не предусмотрено. Вместо этого, для выполнения сложных логических операций используются Q объекты.
Объект класса Q
представляет собой абстракцию для условий фильтрации, которые могут быть легко комбинированы и использованы для создания более сложных запросов к базе данных.
Синтаксис
Класс Q
наследуется от класса Node
, и я не буду забираться сейчас слишком глубоко) на данном этапе достаточно понимать, что класс Q
может принимать сразу несколько условий, как позиционными, так и именованными аргументами. И по умолчанию эти аргументы соединяются между собой условием AND
.
# from django.db.models import Q
class Q(tree.Node):from django.db.models import Q
"""
Encapsulate filters as objects that can then be combined logically (using
`&` and `|`).
"""
# Connection types
AND = "AND"
OR = "OR"
XOR = "XOR"
default = AND
conditional = True
def __init__(self, *args, _connector=None, _negated=False, **kwargs):
super().__init__(
children=[*args, *sorted(kwargs.items())],
connector=_connector,
negated=_negated,
)
...
Например, condition = Q(title__contains='ль', pk__in=(1, 7, 9, 14))
будет иметь следующий словарь атрибутов:
{'children': [('pk__in', (1, 7, 9, 14)), ('title__contains', 'ль')],
'connector': 'AND',
'negated': False}
Где children
- список условий для фильтрации, connector
- логический оператор, которым объединяются между собой условия из children, и negated
- должны ли данные условия отрицаться.
При указании этого условия в фильтре, например, Women.objects.filter(condition)
, Q объект будет преобразован в следующее условие для SQL запроса:
In [32]: Women.objects.filter(condition)
...
WHERE (**"app_women"."id" IN (1, 7, 9, 14)
AND "app_women"."title" LIKE '%ль%'** ESCAPE '\\')
Если при создании Q объекта перед ним указать знак тильды ~
, то атрибут negated
примет значение True
.
condition = ~Q(title__contains='ль')
In [35]: condition.__dict__
Out[35]: {'children': [('title__contains', 'ль')],
'connector': 'AND', **'negated': True**}
И данный объект будет преобразован в условие с оператором NOT
в SQL запросе.
In [36]: Women.objects.filter(condition)
...
WHERE **NOT ("app_women"."title" LIKE '%ль%'** ESCAPE '\\')
Note
_connector и _negated являются защищенными атрибутами, не передавайте им значения напрямую, чтобы не сломать логику работы всего класса!)
Объединение Q объектов
Объекты Q можно объединять с помощью операторов &
- AND, |
- OR и ^
- XOR. В результате такого объединения получается новый Q-объект.
Например, в данном случае я использую оператор pipe |
, и атрибут connector
принимает значение OR
.
In [37]: con1 = Q(title__contains='ль')
In [38]: con2 = Q(pk__in=(1, 7, 14, 18))
In [39]: **condition = con1 | con2**
In [40]: condition.__dict__
Out[40]:
{'children': [('title__contains', 'ль'), ('pk__in', (1, 7, 14, 18))],
**'connector': 'OR'**,
'negated': False}
При использовании данного объекта в методе filter
, мы получим следующее условие в SQL запросе:
In [41]: Women.objects.filter(condition)
...
WHERE ("app_women"."title" LIKE '%ль%' ESCAPE '\\' **OR** "app_women"."id" IN (1, 7, 14, 18))
Использование с lookups
Если мы хотим использовать Q объекты совместно с обычными лукапами по атрибутам объекта, то Q объекты нужно указывать в начале, как позиционные аргументы.
Также важно запомнить, что при перечислении нескольких Q объектов через &
и |
, AND и OR операторы в выражении будут указаны на одном уровне.
In [53]: Women.objects.filter(Q(title__contains='ль') | Q(cat_id=4) & Q(pk__in=(1, 2, 3)))
...
WHERE ("app_women"."title" LIKE '%ль%' ESCAPE '\\'
OR ("app_women"."cat_id" = 4
AND "basefunc_post"."id" IN (1, 2, 3)))
Если же мы используем |
и ,
то условие с оператором OR будет взято в скобки.
In [54]: Women.objects.filter(Q(title__contains='ль') | Q(cat_id=4), Q(pk__in=(1, 2, 3)))
...
WHERE (**("app_women"."title" LIKE '%ль%' ESCAPE '\\' OR "basefunc_post"."cat_id" = 4)**
AND "app_women"."id" IN (1, 2, 3))
Note
Так как мы не можем соединять между собой лукапы по атрибутам с Q объектами, используя что-то кроме запятой, то желательно помнить, что Q объекты соединённые между собой
|
в преобразованных SQL запросах будут взяты в скобки!
Summery
Чтобы получать более сложные условия для фильтрации, используем Q объекты.
С Q объектами можно использовать следующие операторы:
- для
NOT
используем~
; - для
AND
используем&
; - для
OR
используем|
; - для
XOR
используем^
;
Приоритет операторов аналогичен приоритетам в булевой алгебре)
Операторы выше перечислены в соответствии с их приоритетом (наивысший у верхнего).
Можно использовать Q объекты с лукапами атрибутов объекта, но только через запятую и указав Q объекты в начале условия.
〰〰〰 𓆝 𓆟 𓆞 𓆝 𓆟 𓆝 𓆟 𓆞 〰〰〰