Search code examples
djangodjango-modelsdjango-haystack

haystack search with multiple models


In my project, I have two tables (Users, Languages). Each user has at least one 'Language'.

I want to be able to search by language or location, and see the user's name, language/s, and country as a result.

I have the following models:

class UserProfile(models.Model):
    user = models.OneToOneField(User)
    state = models.CharField(max_length=200,default='',blank=True, null=True)
    country = models.CharField(max_length=200,default='',blank=True, null=True)

    def __unicode__(self):
        return unicode(self.user)

class Language(models.Model):
    id = models.AutoField(primary_key=True)
    user = models.ForeignKey('UserProfile')
    language = models.CharField(max_length=200,default='',blank=True, null=True)

    def __unicode__(self):
        return unicode(self.user)

What I've managed to do:

class UserIndex(indexes.SearchIndex, indexes.Indexable):
    text = indexes.CharField(document=True, use_template=True)

    city = indexes.CharField(model_attr='city')
    zip = indexes.CharField(model_attr='zip')
    country = indexes.CharField(model_attr='country')

    def get_model(self):
        return UserProfile
    def index_queryset(self, using=None):
        return self.get_model().objects.all()

My userprofile_text.txt:

{{ object.user.get_full_name }}
{{ object.language}} <-- of course this doesn't work.
{{ object.country}}
{{ object.zip}}

I am able to search using name, zip, and country and display results accordingly How do I modify my code so that I'm also able to search by language? Also, how would I show all languages related to each user?

something like:

{% for language in result.object.languages %}
{{ language }}
{% endfor %}

All I've found on SO so far are outdated answers or answers unrelated to haystack. Any help?


Solution

  • Since you are using a ForeignKey from the Language to the User without a related_name, you need to access language_set on the user object:

    {{ object.user.get_full_name }}
    {% for language in object.user.languages %}
        {{ language }}
    {% endfor %}
    

    This gives you a list of all languages for a user.


    However, the problem is mostly in your modelling. This chunk:

    class Language(models.Model):
        id = models.AutoField(primary_key=True)
        user = models.ForeignKey('UserProfile')
    

    essentially states that "Every language has exactly one user who speaks it". Which is probably not what you want. Instead use a ManyToManyField:

    class Language(models.Model):
        id = models.AutoField(primary_key=True)
        users = models.ManyToManyField('UserProfile',related_name="spoken_languages")
    
    {{ object.user.get_full_name }}
    {% for language in object.user.spoken_languages %}
        {{ language }}
    {% endfor %}
    

    Which allows a user to speak many languages and for a language to be spoken by many users.