2026, Jan 11 09:02
Как исправить ошибку Cannot use None в Django Q: разделяем форму и результаты поиска чатов
Почему в Django возникает ошибка Cannot use None as a query value при фильтрации через Q, и как исправить: разделите форму поиска и страницу результатов.
Искать групповые чаты по названию вроде бы просто, но из‑за мелочи в обработке запроса всё может сломаться ещё до того, как пользователь что‑то введёт. Ниже — короткое объяснение проблемы и рабочий способ избежать ошибки «Cannot use None as a query value», сохраняя для пользователя понятный сценарий.
Краткое описание проблемы
Нужно отфильтровать групповые чаты по текстовому запросу с помощью выражения Q в Django. Ошибка возникает сразу при загрузке страницы, потому что представление выполняет фильтрацию в БД, когда параметр запроса ещё отсутствует.
Модель и представление, воспроизводящие проблему
Ниже — модель и представление для демонстрации. Идентификаторы простые и понятные, логика программы сохранена без изменений.
# models.py
from django.db import models
from django.contrib.auth.models import User
import shortuuid
class GroupRoom(models.Model):
room_key = models.CharField(max_length=128, unique=True, default=shortuuid.uuid)
room_title = models.CharField(max_length=128, null=True, blank=True)
avatar = models.ImageField(upload_to='uploads/profile_pictures', default='uploads/profile_pictures/default.png', blank=True)
bio = models.TextField(max_length=500, blank=True, null=True)
owner = models.ForeignKey(User, related_name='owned_rooms', blank=True, null=True, on_delete=models.SET_NULL)
online_users = models.ManyToManyField(User, related_name='present_in_rooms', blank=True)
participants = models.ManyToManyField(User, related_name='joined_rooms', blank=True)
private_flag = models.BooleanField(default=False)
def __str__(self):
return self.room_key
# views.py
from django.views import View
from django.shortcuts import render
from django.db.models import Q
from .models import GroupRoom
class RoomLookup(View):
def get(self, request, *args, **kwargs):
term = self.request.GET.get('chat-query')
rooms = GroupRoom.objects.filter(
Q(room_key__room_title__icontains=term)
)
payload = { 'rooms': rooms }
return render(request, 'chat/search.html', payload)
Что происходит под капотом
Страница открывается GET‑запросом без строки запроса. Представление всё равно запускает фильтрацию QuerySet и передаёт отсутствующее значение как None. Django отклоняет его в lookup и выбрасывает указанную пользователем ошибку. Корневая причина в том, что обращение к базе выполняется безусловно при первоначальной загрузке, ещё до ввода поискового термина.
использование представления на классах запускало запрос при открытии страницы. Мне пришлось сделать отдельную страницу только с полем ввода, а результаты использовать на другой странице
Этого небольшого изменения достаточно, чтобы поиск в базе не выполнялся, пока нет валидного ввода.
Решение: разделите страницу ввода и страницу результатов
Разделите сценарий на два представления. Первое рендерит форму поиска без обращений к базе. Второе отвечает за результаты и запускает фильтрацию только при наличии параметра запроса.
# views.py
from django.views import View
from django.shortcuts import render
from django.db.models import Q
from .models import GroupRoom
class RoomSearchPage(View):
def get(self, request, *args, **kwargs):
return render(request, 'chat/search_form.html')
class RoomSearchResults(View):
def get(self, request, *args, **kwargs):
term = request.GET.get('chat-query')
if not term:
return render(request, 'chat/results.html', { 'rooms': GroupRoom.objects.none() })
rooms = GroupRoom.objects.filter(
Q(room_key__room_title__icontains=term)
)
return render(request, 'chat/results.html', { 'rooms': rooms })
В такой структуре первый GET, который рендерит форму, не делает запросов. Фильтр вычисляется только на эндпойнте результатов и только при переданном терме, так что значение None вовсе не участвует.
Почему это важно
Защита логики фильтрации от отсутствующих входных данных предотвращает лишние исключения и снимает ненужную нагрузку при загрузке страницы. К тому же путь пользователя становится ясным: сначала ввести запрос, затем увидеть отфильтрованные групповые чаты. Такой UX поиска предсказуем, а поведение на сервере — однозначно.
Итоги
Запуск фильтрации в базе без проверки входных данных приведёт к ошибкам вроде «Cannot use None as a query value». Держите форму поиска и обработку результатов раздельно, чтобы фильтр срабатывал только при осмысленном вводе. Этого небольшого шага достаточно, чтобы стабилизировать представление и добиться ожидаемого поведения поиска по названиям чатов.