Django TypeError: Cannot filter against a non-conditional expression while filtering by foreign key
I've looked through multiple similar questions, but couldn't get the logic, and my manipulations were unsuccessful.
I have a forum app, its structure is "forum->subforum->topic->comments", corresponding models for subforums, topics and comments. I have to implement a filter on each subforum page (where the list of corresponding topics is displayed), by which I can configure the search of/among the topics by several conditions.
Particularly I have to implement a custom filter, and my idea was to create a filter for empty topics, that is, for topics only with initial comments and without responses. Through a desperate struggle I've finally understood how to implement reversed foreign key connection in querysets, but now I can't use a queryset expression as a filter, as it returns TypeError: Cannot filter against a non-conditional expression.
Here are the specifics:
filters.py:
import django_filters
from django.db.models import Count
from forum.models import Topic
class TopicFilter(django_filters.FilterSet):
date_span = django_filters.DateRangeFilter(field_name='created', label='Период создания')
comments = django_filters.CharFilter(field_name='comment__content', lookup_expr='icontains', label='Комментарии')
subject = django_filters.CharFilter(lookup_expr='icontains', label='Тема обсуждения')
creator = django_filters.CharFilter(field_name='creator__username', lookup_expr='icontains', label='Создатель темы')
is_empty = django_filters.BooleanFilter(method='filter_empty', label='Темы без комментариев')
class Meta:
model = Topic
fields = ['subject', 'creator', 'date_span', 'comments', 'is_empty']
def filter_empty(self, queryset, value):
comment_count_gte_1 = Topic.objects.annotate(comment_count=Count('comments')).filter(comment_count__gte=1)
comment_count_lt_1 = Topic.objects.annotate(comment_count=Count('comments')).filter(comment_count__lt=1)
if value is True:
return queryset.filter(comment_count_lt_1)
elif value is False:
return queryset.filter(comment_count_gte_1)
else:
return queryset
The aforementioned approach doesn't work - apparently, one can't provide a link of queryset expression as a filter criteria. But even if to enter the expression itself, that is:
def filter_empty(self, queryset, value):
if value is True:
return queryset.filter(Topic.objects.annotate(comment_count=Count('comments')).filter(comment_count__lt=1))
elif value is False:
return queryset.filter(Topic.objects.annotate(comment_count=Count('comments')).filter(comment_count__gte=1))
else:
return queryset
then it doesn't work either.
Comparing it the way like:
def filter_empty(self, queryset, value):
comment_count = Topic.objects.annotate(comment_count=Count('comments'))
if value is True:
return queryset.filter(comment_count < 1)
elif value is False:
return queryset.filter(comment_count >= 1)
else:
return queryset
returned another error: TypeError: '>=' not supported between instances of 'QuerySet' and 'int'.
And now I have difficulties in elaborating a proper filter expression for a potential queryset. Thus I came here to ask for help and advice.
If any additional info/files are necessary, I'm ready to provide them.
You .filter(…)
[Django-doc] on an .annotate(…)
[Django-doc] with:
def filter_empty(self, queryset, name, value):
queryset = Topic.objects.annotate(comment_count=Count('comments'))
if value is True:
return queryset.filter(comment_count__lt=1)
elif value is False:
return queryset.filter(comment_count__gte=2)
else:
return queryset