StackOverflow helps me a lot for the completion of the following project. However, I am stuck in a point --> Haystack Facets! I have read dozens of questions-answers but none satisfies my case.
I am using Django to build an e-shop which will sell jewelry, small statues, artistic things etc. I also using django-mptt snippet to organize my categories.
What I want (for facet implementation only) is something like this. Thus, different facets depending on the category chosen. I have concluded that in order to achieve this I have to set a different SearchQuerySet in MyFacetedSearchView
's __init__
depending on the category the user clicked. How can I do that? Am I mistaken or not?
My files:
#search_indexes.py
from haystack import indexes
from .models import Product
class ProductIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
creator = indexes.CharField(model_attr='creator', faceted=True)
material = indexes.CharField(model_attr='material', null=True, faceted=True)
category = indexes.MultiValueField(faceted=True)
sizevenus = indexes.MultiValueField(null=True, faceted=True)
def get_model(self):
return Product
def prepare_category(self, obj):
"""
Prepares the categories for indexing.
obj.categories.all() runs for each Product instance.
Thus, if we have 10 products then this method will run 10 times (during rebuild_index or update_index command)
creating each time a different list for the categories each product belongs to.
"""
return [category.slug for category in obj.categories.all()]
def prepare_sizevenus(self, obj):
"""
Same philosophy applies here for the size of the product. But this time we have explicitly told that
we want the size for the VENUS products ONLY. The rest of the products of this e-shop have no sizes!
"""
return [stock.size.name for stock in obj.productstock_set.filter(product__categories__slug='venus')]
def index_queryset(self, using=None):
"""
This method defines the content of the QuerySet that will be indexed. It returns a list of Product instances
where each one will be used for the prepare_***** methods above.
"""
return self.get_model().objects.all()
#views.py
class ShowProductsByCategory(FacetedSearchView):
def __init__(self):
sqs = SearchQuerySet().facet('category').facet('creator').facet('sizevenus').facet('
template = 'catalog/show_products_by.html'
form_class = MyFacetedSearchForm
super(ShowProductsByCategory, self).__init__(template=template, searchqueryset=sqs, form_class=form_class)
The problem:
When ShowProductsByCategory
view is initialized it gets the whole sqs. Then in all of my pages (jewelry, ceramic, statues etc.) the facets show the products from the whole catalogue, not the specific category I am in, i.e in jewelry page it shows all the jewelry-related products but in the facets (by creator) div, it shows a creator A that has built jewels and a creator B that has not (but B has built say, statues).
How can I pass different SearchQuerySet
each time in order to organize my facets?
OK, After 5 days I have managed to figure it out.
The problem was that I wanted a different group of facets to appear depending on the page.
The solution lies within the extra_content
method of FacetedSearchView
class of Haystack. Inside this method the extra['facets']
key (of the dict extra
) is defined.
The only thing I had to do was to override this method in my views
and define a different facet_counts()
group depending on the category I'm in
So, the code flow is this:
extra_content
method of FacetedSearchView
is called and the extra['facets'] = self.results.facet_counts()
key-value is defined.views
, I have also declared extra['facets'] = something_else.facet_counts()
which is the final value..facet_counts()
is rendered to the template and I have what I want.
Maybe this will help someone else!