Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Django ORM __in but instead of exact, contains case insensative?

I am currently trying to use the Django ORM to query the Recipe model’s ingredients.

class Recipe(models.Model):
    account = models.ForeignKey(CustomUser, on_delete=models.CASCADE, null=True, blank=True)
    name = models.TextField(null=True, blank=True)

class RecipeIngredients(models.Model):
    recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE, null=True)
    ingredient = models.TextField(null=True, blank=True)

What I have so far is


ingredients = ["eggs", "bacon", "potato"]
recipes = Recipe.objects.filter(
    recipeingredients__ingredient__in=ingredients
).alias(
    ningredient=Count('recipeingredients')
).filter(
    ningredient__gte=len(ingredients)
)

From my understanding of this answer this will return all the items that contain only "eggs", "bacon", and "potato", but not say Eggs or Scrambled EGGS. Is there anyway to adjust this to have it search for all items that contains the ingredients and case insensative?

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

You can create a disjunction of conditions, with:

from django.db.models import Q

ingredients = ['eggs', 'bacon', 'potato']

recipes = Recipe.objects.filter(
    Q(
        *[('recipeingredients__ingredient__icontains', ingredient) for ingredient in ingredients],
        _connector=Q.OR
    )
).alias(
    ningredient=Count('recipeingredients')
).filter(
    ningredient__gte=len(ingredients)
)

A potential problem might be that if the query is eggs, and two ingredients match, like 'white eggs' and 'brown eggs', these will count as two, and therefore another ingredient, like bacon might not be an ingredient, and will still be part of the QuerySet, so unfortunately, it is not easy to make a QuerySet that matches exactly all ingredients.

A potential solution could be:

ingredients = ['eggs', 'bacon', 'potato']

recipes = Recipe.objects.all()

for ingredient in ingredients:
    recipes = recipes.filter(recipeingredients__ingredient__icontains=ingredient)

But this will make n JOINs for n ingredients, and thus can easily become infeasible for a large amount of ingredients.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading