← Назад к вопросам

Как реализуется логика "или" в querySet в Django?

1.8 Middle🔥 221 комментариев
#Django#Базы данных (SQL)

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Логика "ИЛИ" в Django QuerySet

Для реализации логики "или" (OR) в Django ORM используется объект Q. Это мощный инструмент для построения сложных условий в querySet.

1. Основной синтаксис с Q

from django.db.models import Q
from myapp.models import Article

# Найти статьи, где title содержит "Django" ИЛИ content содержит "Python"
result = Article.objects.filter(
    Q(title__contains="Django") | Q(content__contains="Python")
)

# SQL эквивалент:
# SELECT * FROM article WHERE title LIKE '%Django%' OR content LIKE '%Python%'

2. Комбинирование AND и OR

OR (|) имеет приоритет над AND (&):

# (title contains "Django" OR author is John) AND published is True
result = Article.objects.filter(
    (Q(title__contains="Django") | Q(author__name="John")),
    published=True
)

# SQL:
# SELECT * FROM article 
# WHERE (title LIKE '%Django%' OR author_id = john_id) AND published = true

Использование & для AND:

result = Article.objects.filter(
    Q(title__contains="Django") & Q(author__name="John")
)

# SQL: WHERE title LIKE '%Django%' AND author_id = john_id

3. Отрицание (NOT) с ~

# Статьи, которые НЕ содержат "спам" И НЕ неопубликованы
result = Article.objects.filter(
    ~Q(title__contains="spam"),
    ~Q(published=False)
)

# SQL:
# SELECT * FROM article 
# WHERE title NOT LIKE '%spam%' AND published = true

4. Сложные условия

# Найти статьи:
# (категория = "Tech" И (автор="John" ИЛИ автор="Jane")) 
# ИЛИ (опубликовано В течение последних 7 дней)

from datetime import timedelta
from django.utils import timezone

result = Article.objects.filter(
    Q(
        Q(category="Tech") & (Q(author__name="John") | Q(author__name="Jane"))
    ) |
    Q(published_date__gte=timezone.now() - timedelta(days=7))
)

5. Практические примеры

Поиск пользователей по email или username:

from django.contrib.auth.models import User

user = User.objects.get(
    Q(email="test@example.com") | Q(username="testuser")
)

Фильтрация товаров в интернет-магазине:

# Товары дорогие (>1000) И (в наличии ИЛИ доступны для заказа)
products = Product.objects.filter(
    Q(price__gt=1000),
    Q(in_stock=True) | Q(available_for_order=True)
)

Поиск комментариев:

# Комментарии (от администратора ИЛИ спам) И опубликованы
comments = Comment.objects.filter(
    (Q(author__is_staff=True) | Q(is_spam=True)),
    published=True
)

6. Динамическое построение Q

Часто нужно динамически собирать условия:

from django.db.models import Q

def search_articles(keywords=None, authors=None, published_only=False):
    query = Q()  # Начинаем с пустого Q
    
    # Добавляем условия по ключевым словам (OR)
    if keywords:
        for keyword in keywords:
            query |= Q(title__icontains=keyword) | Q(content__icontains=keyword)
    
    # Добавляем условия по авторам (OR)
    if authors:
        author_query = Q()
        for author in authors:
            author_query |= Q(author__name=author)
        query &= author_query
    
    # Фильтр по опубликованности (AND)
    if published_only:
        query &= Q(published=True)
    
    return Article.objects.filter(query)

# Использование
articles = search_articles(
    keywords=["Django", "Python"],
    authors=["John", "Jane"],
    published_only=True
)

7. Исключение (exclude) с Q

# Исключить статьи, которые либо спам, либо неопубликованы
result = Article.objects.exclude(
    Q(is_spam=True) | Q(published=False)
)

# Эквивалентно:
result = Article.objects.filter(
    ~(Q(is_spam=True) | Q(published=False))
)

# SQL: WHERE NOT (is_spam = true OR published = false)
#      => WHERE is_spam = false AND published = true

8. Оптимизация с exists() и count()

# Найти авторов, у которых ЕСТЬ (опубликованные статьи ИЛИ спам)
from django.db.models import Exists, OuterRef

result = Article.objects.filter(
    Q(published=True) | Q(is_spam=True)
).values('author').distinct()

# Или с Exists для более сложных случаев:
spam_or_published = Article.objects.filter(
    author=OuterRef('pk'),
    Q(published=True) | Q(is_spam=True)
)

authors_with_spam_or_published = User.objects.filter(
    Exists(spam_or_published)
)

9. Производительность

Плохо — выполнит 2 отдельных запроса и объединит в памяти:

result1 = Article.objects.filter(title__contains="Django")
result2 = Article.objects.filter(author__name="John")
result = result1 | result2  # Union в памяти (медленно)

Хорошо — один SQL запрос:

result = Article.objects.filter(
    Q(title__contains="Django") | Q(author__name="John")
)  # Один SELECT с OR в WHERE

10. Проверка SQL с explain()

# Django 3.1+
query = Article.objects.filter(
    Q(title__contains="Django") | Q(published=True)
)
print(query.explain())  # Вывести план выполнения

Итоговая таблица операций

Опера­ция  | Синтаксис      | Значение         | SQL
-----------|----------------|------------------|-------------------
ILI (OR)   | Q1 | Q2        | Q1 ИЛИ Q2      | WHERE ... OR ...
I (AND)    | Q1 & Q2        | Q1 И Q2        | WHERE ... AND ...
NOT        | ~Q1            | НЕ Q1          | WHERE NOT (...)
Исключить  | .exclude(Q)    | НЕ Q           | WHERE NOT (...)

Правила использования

  1. Используй Q для OR условий — filter() по умолчанию работает как AND
  2. Правильно расставляй скобки — приоритет операций важен
  3. Избегай дублирования — используй Exists() для сложных условий
  4. Проверяй SQL — используй str(query.query) или .explain()
  5. Оптимизируй запросы — используй select_related() / prefetch_related()

Q объекты — это ключ к мощным и эффективным фильтрам в Django ORM.

Как реализуется логика "или" в querySet в Django? | PrepBro