Search code examples
djangodjango-haystackfaceted-search

Different SearchQuerySet depending on the page


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?


Solution

  • 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:

    1. First, the extra_content method of FacetedSearchView is called and the extra['facets'] = self.results.facet_counts() key-value is defined.
    2. Because this method is overridden in my views, I have also declared extra['facets'] = something_else.facet_counts() which is the final value.
    3. Finally, my .facet_counts() is rendered to the template and I have what I want.

    Maybe this will help someone else!