Search code examples
pythondjangomanytomanyfield

Django manytomany field, how to get check for 80% subset/ match?


I have a Django db with cooking recipes. I want to query all users who have at least 80% ingredients to make a recipe. How do I achieve this?

Or how do I query for users who are missing only 1 ingredient for the recipe?

models.py

class ingredient(models.Model):
    id = models.AutoField("id",max_length = 100, primary_key=True)
    name=models.CharField("Ingredient", max_length=100)

    def __unicode__ (self):
        return self.name

class user(models.Model):
    id = models.AutoField("id",max_length = 100, primary_key=True
    ingredient = models.ManyToManyField(ingredient,blank=True)

    def __unicode__ (self):
        return str(self.id)

class recipe(models.Model):
    id = models.AutoField("id", max_length=100, primary_key=True)
    recipe_ingredient = models.ManyToManyField(ingredient,related_name='recipe_ingredient',blank=True)

    def __unicode__ (self):
        return self.id

Solution

  • I would use django-haystack for this, you can setup an index for your job_opening model and index all the ingredients:

    class JobOpeningIndex(indexes.SearchIndex, indexes.Indexable):
        text = indexes.CharField(document=True, use_template=True)
        recipe_ingredient = indexes.MultiValueField()
    
        def prepare_recipe_ingredient(self, obj):
            return [i.name for i in obj.recipe_ingredient.all()]
    
        def get_model(self):
            return job_opening
    
        def index_queryset(self, using=None):
            return self.get_model().objects.all()
    

    After the indexes has been created you can just do a filter by ingredient or list of ingredients, haystack will return all objects which match the search criteria, sorted by relevance. You can then just map the 80% to a score in relevance and filter out the items with a relevancy lower that your required amount.

    SearchQuerySet().models(job_opening).filter(recipe_ingredient__in=['ingredient1', 'ingredient2', 'ingredient3'])
    

    EDIT

    After reading the edited question, we basically have to use recipe where we used job_opening. And it seems like you want to find relevant users, not recipes. To accomplish this, you would have to index the user model instead of the original job_opening model:

    class UserIndex(indexes.SearchIndex, indexes.Indexable):
        text = indexes.CharField(document=True, use_template=True)
        ingredient = indexes.MultiValueField()
    
        def prepare_ingredient(self, obj):
            return [i.name for i in obj.ingredient.all()]
    
        def get_model(self):
            return user
    
        def index_queryset(self, using=None):
            return self.get_model().objects.all()
    

    After the index has been created, you can then do the filter like this:

    myrecipe = recipe.objects.get(...)
    SearchQuerySet().models(user).filter(ingredient__in=myrecipe.recipe_ingredient.all())