选择那些在给定字符串中相关对象ID为* all *的对象

时间:2010-04-30 19:45:16

标签: sql django django-queryset

我想建立一个配方数据库的前端,使用户能够搜索可以用用户提供的成分烹饪的食谱列表。

我有以下型号

class Ingredient(models.Model):
    name = models.CharField(max_length=100, unique=True)
    slug = models.SlugField(max_length=100, unique=True)
    importancy = models.PositiveSmallIntegerField(default=4)
    […]

class Amount(models.Model):
    recipe = models.ForeignKey('Recipe')
    ingredient = models.ForeignKey(Ingredient)
    […]

class Recipe(models.Model):
    name = models.CharField(max_length=100)
    slug = models.SlugField()
    instructions = models.TextField()
    ingredients = models.ManyToManyField(Ingredient, through=Amount)
    […]

和一个完全符合我想要的原始查询:它获取用户提供的字符串列表中包含所需全部所需的所有食谱。如果他提供的不仅仅是必要的,那也没关系。

query = "SELECT *, 
    COUNT(amount.zutat_id) AS selected_count_ingredients, 
    (SELECT COUNT(*) 
            FROM amount 
            WHERE amount.recipe_id = amount.id) 
    AS count_ingredients 
    FROM amount LEFT OUTER JOIN amount 
    ON (recipe.id = recipe.recipe_id) 
    WHERE amount.ingredient_id IN (%s) 
    GROUP BY amount.id 
    HAVING count_ingredients=selected_count_ingredients" % 
            ",".join([str(ingredient.id) for ingredient in ingredients])
recipes = Recipe.objects.raw(query)

现在,我正在寻找的是一种不依赖于.raw()的方式,因为我希望纯粹使用Django的queryset方法。

此外,如果你们知道一种在查找中包含成分的重要性的方法,那么即使它的一个成分(具有0的重要性)未被提供,结果仍然显示为结果,这将是非常棒的由用户。

2 个答案:

答案 0 :(得分:1)

看起来你为这个设置模型做得非常好。您可以在两个非常简单的查询中执行此操作,这些查询的运行速度可能与使用连接的单个查询一样快。

amounts = Amount.objects.filter(ingredient__in=ingredients)
rezepte = Rezept.objects.filter(
        pk__in=amounts.values_list('recipe', flat=True)
    ).order_by('importancy')

如果您愿意,甚至可以通过将内存放在一个语句中来节省内存:

rezepte = Rezept.objects.filter(
        pk__in=Amount.objects.filter(
                ingredient__in=ingredients
        ).values_list('recipe', flat=True)
    ).order_by('importancy')

默认情况下,两个查询都应自动从django中获取索引。你最终会得到两个非常干净的查询:

SELECT `yourproject_amount`.`recipe_id` FROM `yourproject_amount`;
SELECT * FROM `yourproject_rezept`
        WHERE `yourproject_rezept`.`id` IN (1, 2, 3, 4)
        ORDER BY `yourproject_rezept`.`importancy`;

答案 1 :(得分:1)

您可以使用连接查询集的可能性。

最简单的方法(但效率不高,因为在sql中多次加入Amount表...):

ingredients = Ingredient.objects.filter(...)
query = Recipe.object.all()
for i in ingredients:
    query = query.filter(ingredients=i)


print query # at this database query will be exceuted