Search code examples
pythondjangodjango-haystack

Search from a Foreign Key in Django Haystack


I have these two models:

class Shop(CustomBaseModel):
    username = models.CharField(max_length=200)
    bio = models.CharField(max_length=200)


class Item(CustomBaseModel):
    shop = models.ForeignKey(Shop)
    tags = models.TextField()

And I have this index in search_indexes.py:

class ItemIndex(indexes.SearchIndex, indexes.Indexable):
    text = indexes.CharField(document=True, use_template=True)
    tags = indexes.CharField(model_attr='tags')

And this item_text.txt file:

{{ object.tags}}

This is my view:

def search(request):
    form = ItemSearchForm(request.GET)
    results = form.search()
    return render(request, 'search/search.html', {
        'items': results
    })

This is my form:

class ItemSearchForm(SearchForm):
    def search(self):
        sqs = super(ItemSearchForm, self).search().models(Item, Shop)
        return sqs

This is the template:

 {% if items|length > 0 %}
        <h1>Items</h1>
        {% for item in items %}
            <p>
                {{ item.tags }}
            </p>
        {% endfor %}
 {% endif %}

This is searching from tags and working fine. But how can I display all items that belong to a shop if someone includes a username or bio of that shop?


Solution

  • When using Haystack and you got the result query set you have a list of objects, the fields of this objects depends on what you've wrote in:

    templates/search/indexes/project_name/modelName_text.txt
    

    But there is allways a field that can help you here, the field object

    If I understand your question you want to know:

    • How to show all items of a shop when an user look for a shop using its username or bio

    I'll show you how to do it in command line, you can apply this code to your view/template:

    from haystack.query import SearchQuerySet
    
    # First we make a search to get some shops 
    # assuming you defined username field in search_indexes.py for Shop object
    shop_list = SearchQuerySet().filter(username='something')  
    # or
    shop_list = SearchQuerySet().filter(content='shop_username')  
    # Now (let's supose there is at least 1 shop) you can get the items like:    
    shop_list[0].object  # This returns the shop object, so you can do
    shop_list[0].object.item_set.all()  # This returns all items belonging to this shop
    

    In your template you could try:

    {{ object.object.item_set.all }}  # This return a list of items belonging to shop
    

    You could generate a list like this

    <ul>
    {% for item in object.object.item_set.all %}
         <li>{{item}}</li>
    {% endfor %}
    </ul>
    

    This can be confused because you called object to your variable, but you need to remember that a SearchQuerySet instance allways have this object field to access the original Django object

    I recommend you to check Haystack - SearchQuerySet API


    Edit

    In your template your results are in the variable items so you can do something like:

    # Assumming all the items are shops
    {% for shop in items %}  
       {{shop.object.item_set}}  # If the item is a SHop, this return all its items
    {% endfor %}
    

    To separate the results, because I supose not all the items are shops, you can separate the results and send the shops in another variable to the template like:

    def search(request):
        form = ItemSearchForm(request.GET)
        results = form.search()
        return render(request, 'search/search.html', {
            'items': results
            'shops': results.model(Shop)
        })
    

    So in your template you can do:

    {% for shop in shops %}  
       {{shop.object.item_set}} 
    {% endfor %}
    

    Edit2

    Okay now it looks you have items in your template... and you want all the items of a shop ?

    • Wich shop ? Same shop as the items in template ?

    You said:

    But how can I display all items that belong to a shop if someone includes a username or bio of that shop?

    So you're using haystack to search for items, but want the results to be shops ?

    You could add into your item_text.txt file:

    {{ object.tags}}
    # If you add this and you look for shop username 
    # or bio it will return all items of the shop
    {{ object.shop.username}} 
    {{ object.shop.bio}} 
    # Adding this 2 fields items are indexed with the related shop username and bio
    

    or you can start indexing Shop model too.