【问题标题】:Django Query for Total Count of Records In ManyToMany FieldDjango 查询多对多字段中的记录总数
【发布时间】:2019-05-31 15:45:51
【问题描述】:

给定两个由 ManyToMany 连接的模型,但没有通过表:

class Ingredient(models.Model):
    name = models.CharField(max_length=255)

class Recipe(models.Model):
    name = models.CharField(max_length=255)
    ingredients = models.ManyToManyField(Ingredient)

如何找到配方中使用的某种成分的实例总数?

例如,如果我有两种配料(苹果和糖)和两种食谱(苹果派和甜甜圈),我怎么知道食谱有 3 种用途(两种因为苹果派使用苹果和糖,一种因为甜甜圈用糖)?

我可以通过以下方式做到这一点:

count = 0
for recipe in Recipe.objects.all():
  count += recipe.ingredients.count()

但这会产生太多查询。

有没有一种简单的方法可以通过注释/聚合获得这个数字?

【问题讨论】:

    标签: django django-models


    【解决方案1】:

    我们可以这样尝试(当然是为了避免大量DB 命中。使用数据库聚合)。

    from django.db.models import Count
    
    recipes = Recipe.objects.annotate(count_ingredients=Count('ingredients'))
    for recipe in recipes:
        print(recipe.pk, recipe.count_ingredients)
    

    【讨论】:

      【解决方案2】:
      Recipe.ingredients.through.objects.filter(ingredient=YOUR_INGREDIENT_HERE).count()
      

      Recipe.through 是保存 many_to_many 字段对象的“秘密”表,这是一个新的 Recipe_ingredients(django 的默认名称)对象被创建。如果您想使用给定成分的食谱数量,您只需使用您的成分过滤该表并获取它的数量。

      对于您的示例,这些是创建的:(伪)

      Recipe_ingredients(ingredient=sugar, recipe=apple_pie)
      Recipe_ingredients(ingredient=sugar, recipe=doughnut)
      Recipe_ingredients(ingredient=apple, recipe=apple_pie)
      

      从这里你可以用这张表计算任何东西,如果你想知道所有成分的总用途,它就像

      Recipe.ingredients.through.objects.count()
      

      【讨论】:

      • 我正在使用我自己的模型和 ManyToMany 相关表来解决这个问题,我发现要获得 through 参考,我基本上必须这样做:Recipe.ingredients.through.objects.count()。你有什么版本的Django?我目前在 3.2。
      • 我写的代码不正确,你的是,我不知道以前怎么没有人意识到这个巨大的错误,大声笑。所有m2m字段都有自己的through字段,所以是Model.m2m_field.through。感谢指正!
      • 顺便说一句,我正在尝试解决一个问题,即我们在高级搜索界面中拥有这些多表复合视图。它的复杂之处在于您可以创建 and-groups 和 or-groups,并且表单提交会将其全部转换为单个 Q 表达式,并且查询是根表的过滤器。我们希望通过 3 个 M:M 关系中的 1 个来拆分行以反映真正的连接。考虑到它的编写方式,根模型是唯一引用的模型,使用 through 将是一个巨大的重写......有没有办法将 through 指定为 Q 表达式的一个组件?
      • ...我对此表示怀疑,但问起来也无妨。只是试图减少重写的工作量。简单地明确定义链接表可能更容易。
      • 有可能,通过模型是一个有两个外键的模型,你可以使用它上面的查找语法来遍历关系,但是你需要检查通过表的默认反向名称是什么或您可以显式创建表并设置反向名称。然后就像recipe__ingredients_through_reverse_to_recipe__ingredients_through_reverse_to_ingredients__ingredient_field 一样简单
      【解决方案3】:
      recipes = Recipe.objects.all()
      for recipe in recipes:
          print(recipe.ingredient_set.count())
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-07
        • 2011-03-25
        • 2013-05-17
        相关资源
        最近更新 更多