Hello! I want to create faceted search and filter.
I use django-filter and postgres full-text search for this. It works good (I mean search and filter). But I don’t know how to build faceted search. In this case, for language, book_type and subject. It will be wonderful if it can count facet. Is it possible? Can you give any advice? Perhaps it is better to do it another way if there will be postgres full-text search?
It takes to me a lot of time. I tried to use only views for this, but it does not work well. Also, I use django-haystack. It works well. But there is no support postgres full-text search.
models.py
class Book(models.Model):
title = models.CharField(max_length=255)
author = models.ForeignKey(
"users.CustomUser",
on_delete=models.SET_NULL,
null=True,
blank=True,
)
coauthor = models.ManyToManyField(
"users.CustomUser",
blank=True,
related_name="coauthor",
)
abstract = models.TextField(blank=True, null=True)
content = models.TextField(blank=True, null=True)
language = models.ForeignKey("Language", on_delete=models.SET_NULL, null=True)
book_type = models.ForeignKey(
"Book_type", on_delete=models.SET_NULL, null=True, blank=True
)
subject = TreeManyToManyField("Subject")
class Language(models.Model):
name = models.CharField(max_length=70, null=True, blank=True)
def __str__(self):
return self.name
class Meta:
ordering = ["-name"]
class Book_type(models.Model):
name = models.CharField(max_length=70, null=True, blank=True)
def __str__(self):
return self.name
class Meta:
ordering = ["-name"]
class Subject(MPTTModel):
name = models.CharField(
max_length=1000,
unique=True,
verbose_name=_("category name"),
help_text=_("format: required, max-100"),
)
filters.py
class BookFilter(django_filters.FilterSet):
search = django_filters.CharFilter(
method="my_custom_filter",
widget=TextInput(
attrs={
"class": "form-control form-search",
"placeholder": _("Type to search"),
}
),
)
language = django_filters.ModelMultipleChoiceFilter(
field_name="language",
queryset=Language.objects.order_by("name"),
widget=forms.CheckboxSelectMultiple(),
)
book_type = django_filters.ModelMultipleChoiceFilter(
field_name="article_type",
queryset=Book_type.objects.all(),
widget=forms.CheckboxSelectMultiple(),
)
subject = django_filters.ModelMultipleChoiceFilter(
field_name="subject",
queryset=Subject.objects.all(),
widget=autocomplete.ModelSelect2Multiple(
url="subject", attrs={"data-placeholder": _("Choose subject")}
),
)
def my_custom_filter(self, queryset, name, value):
q = value
vector = (
SearchVector("title", weight="A")
+ SearchVector("author__first_name", weight="A")
+ SearchVector("author__last_name", weight="A")
+ SearchVector("coauthor__first_name", weight="A")
+ SearchVector("coauthor__last_name", weight="A")
+ SearchVector("abstract", weight="C")
)
return (
queryset.annotate(
rank=SearchRank(vector, q),
)
)
views.py
def BookListView(request):
book = Book.objects.all()
filter = BookFilter(request.GET, queryset=book)
# pagination
book = filter.qs
paginator = Paginator(book, 10)
page = request.GET.get("page", 1)
try:
books = paginator.page(page)
except PageNotAnInteger:
books = paginator.page(1)
except EmptyPage:
books = paginator.page(paginator.num_pages)
context = {
"filter": filter,
"profile": profile,
"books": books,
}
return render(request, "books.html", context)