使用Django“through”字段查找标记的多个实例

时间:2009-12-17 16:17:43

标签: django

我运行一个实验室注释网站,用户可以使用与疾病,组织类型等相关的标签来注释样本。这是一个来自models.py的简单示例:

from django.contrib.auth.models import User
from django.db import models


class Sample(models.Model):
    name = models.CharField(max_length = 255)
    tags=models.ManyToManyField('Tag', through = 'Annot')

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

class Annot(models.Model):
    tag = models.ForeignKey('Tag')
    sample = models.ForeignKey('Sample')
    user = models.ForeignKey(User, null = True)
    date = models.DateField(auto_now_add = True)

我正在寻找django的ORM中的查询,该查询将返回两个用户同意相同标签注释的标签。如果我可以提供用户列表来限制我的查询(如果有人只相信User1和User2并希望找到只有他们同意的样本/标签对,那将会很有帮助。)

1 个答案:

答案 0 :(得分:1)

我想我理解你的需要。这个让我想到了,谢谢! : - )

我相信等效的SQL查询类似于:

select t.name, s.name, count(user_id) count_of_users
  from yourapp_annot a, yourapp_tag t, yourapp_sample s 
 where a.tag_id = t.id 
   and s.id = a.sample_id
group by t.name, s.name
having count_of_users > 1

当我想出django模型导航时,我努力不去思考SQL(它往往会妨碍);当谈到聚合查询时,它总是帮助我可视化SQL的内容。

在django,我们现在有aggregations

以下是我提出的建议:

models.Annot.objects.select_related().values(
  'tag__name','sample__name').annotate(
  count_of_users=Count('user__id')).filter(count_of_users__gt=1)

结果集将包含标记,样本以及使用所述标记标记所述样本的用户数。

为那些不习惯django聚合的人分开:

models.Annot.objects.select_related()

  • select_related()强制在同一查询中检索与Annot相关的所有表
  • 这是允许我在values()调用中指定tag__namesample__name的原因

values('tag__name','sample__name')

  • values()限制要检索到tag.name和sample.name
  • 的字段
  • 这可确保我对客户端数量的汇总仅按这些字段进行分组

annotate(count_of_users=Count('user__id'))

  • annotate()将聚合作为额外字段添加到查询

filter(count_of_users__gt=1)

  • 最后我过滤了总计数。

如果您想要添加额外的过滤器来考虑用户应该考虑的因素,您需要这样做:

models.Annot.objects.filter(user=[... list of users...]).select_related().values(
  'tag__name','sample__name').annotate(
  count_of_users=Count('user__id')).filter(count_of_users__gt=1)

我认为就是这样。


一件事......请注意,我在上面的查询中使用了tag__name和sample__name。但是您的模型未指定标记名称和样本名称为unique

它们应该是独一无二的吗?将unique=True添加到模型中的字段定义。

它们不应该是独一无二的吗?您需要在上面的查询中用tag__id和sample__id替换tag__name和sample__name。