Django用交集计数注释查询集

时间:2018-05-27 19:36:47

标签: python django django-models django-queryset

Djangonauts,我需要挖掘你的大脑。

简而言之,我有以下三种模式:

class Location(models.Model):
    name = models.CharField(max_length=100)


class Profile(models.Model):
    locations_of_interest = models.ManyToManyField(Location)


class Question(models.Model):
    locations = models.ManyToManyField(Location)

我想查找所有感兴趣的位置与特定问题指定的位置相交的所有配置文件。这很简单:

question = Question.objects.first()

matching_profiles = Profile.objects.filter(
    locations_of_interest__in=question.locations.all()
)

但此外,我还想知道地点重叠的范围。

在普通的python中,我可以这样做:

question_location_names = [l['name'] for l in question.locations.all()]

for profile in matching_profiles:
    profile_location_names = [l['name'] for l in profile.locations_of_interest.all()]
    intersection = set(question_location_names).intersection(profile_location_names)
    intersection_count = len(list(intersection))
    # then proceed with this number

但是,如果可能的话,在我看来有利于直接在数据库中进行操作。

TL; DR

所以我的问题是:

有没有办法用这个交集计数注释配置文件查询集,那样做数据库中的操作?

我尝试过几件事情,但我不认为他们对那些阅读此内容并且可能知道答案的人有所帮助。

1 个答案:

答案 0 :(得分:1)

您可以使用.annotate(..)Count(..)号码locations_of_interest执行此操作:

from django.db.models import Count

matching_profiles = Profile.objects.filter(
    locations_of_interest__in=question.locations.all()
).annotate(
    locnom=Count('locations_of_interest')
)

现在每个matching_profiles实例都会有一个名为locnom的属性,其中包含与过滤器匹配的兴趣位置数。

请注意,没有此类位置的Profile将不在查询集中,并且每Profile次最多只会出现一次。

编辑:计算多个相关的非重叠(!)字段

您可以使用distinct=True

计算非重叠联接来扩展此方法
from django.db.models import Count

matching_profiles = Profile.objects.filter(
    locations_of_interest__in=question.locations.all(),
    industries_of_interest__in=question.industries.all()
).annotate(
    locnom=Count('locations_of_interest', distinct=True),
    indnom=Count('industries_of_interest', distinct=True)
)

但是请注意,此方法通常使用JOIN的数量指数进行扩展,因此,如果您要添加数百个,则通常可扩展注释。