简体   繁体   English

(model → FK → model) 关系上的 Django 注释

[英]Django annotation on (model → FK → model) relation

Galaxies across the universe host millions/billions of stars, each belonging to a specific type, depending on its physical properties (Red stars, Blue Supergiant, White Dwarf, etc).宇宙中的星系拥有数百万/数十亿颗恒星,每颗恒星都属于特定类型,具体取决于其物理特性(红星、蓝超巨星、白矮星等)。 For each Star in my database, I'm trying to find the number of distinct galaxies that are also home for some star of that same type.对于我的数据库中的每颗恒星,我试图找到同样类型的恒星所在的不同星系的数量。

class Galaxy(Model):
    ...

class Star(Model):
     galaxy = ForeignKey(Galaxy, related_name='stars')
     type = CharField(...)

Performing this query individually for each Star might be comfortably done by:可以通过以下方式轻松地为每个 Star 单独执行此查询:

star = <some_Star>

desired_galaxies = Galaxy.objects.filter(stars__type=star.type).distinct()
desired_count = desired_galaxies.count()

Or even, albeit more redundant:甚至,虽然更加多余:

desired_count = Star.objects.filter(galaxy__stars__type=star.type).values('galaxy').distinct()

This get a little fuzzier when I try to get the count result for all the stars in a "single" query:当我尝试在“单个”查询中获取所有星星的计数结果时,这会变得有点模糊:

all_stars = Star.objects.annotate(desired_count=...)

The main reason I want to do that is to be capable of sorting Star.objects.order_by('desired_count') in a clean way.我想这样做的主要原因是能够以干净的方式对Star.objects.order_by('desired_count')进行排序。

What I've tried so far:到目前为止我尝试过的:

Star.annotate(desired_count=Count('galaxy', filter=Q(galaxy__stars__type=F('type')), distinct=True))

But this annotates 1 for every star.但这为每颗星注释了1 I guess I'll have to go for OuterRef, Subquery here, but not sure on how.我想我将不得不在这里使用OuterRef, Subquery ,但不确定如何使用。

您可以使用GROUP BY来获取计数:

Star.objects.values('type').annotate(desired_count=Count('galaxy')).values('type', 'desired_count')

Django doesn't provide a way to define multi-valued relationships between models that don't involve foreign keys yet. Django 没有提供一种方法来定义不涉及外键的模型之间的多值关系。 If it did you could do something like如果是这样,你可以做类似的事情

class Galaxy(Model):
    ...

class Star(Model):
    galaxy = ForeignKey(Galaxy, related_name='stars')
    type = CharField(...)
    same_type_stars = Relation(
        'self', from_fields=('type',), to_fields=('type',)
    )

Star.objects.annotate(
    galaxies_count=Count('same_type_stars__galaxy', distinct=True)
)

Which would result in something along这会导致一些事情

SELECT
  star.*,
  COUNT(DISTINCT same_star_type.galaxy_id) galaxies_count
FROM star
LEFT JOIN star same_star_type ON (same_star_type.type = star.type)
GROUP BY star.id

If you want to achieve something similar you'll need to use subquery for now如果你想实现类似的东西,你现在需要使用子查询

Star.objects.annotate(
    galaxies_count=Subquery(Star.objects.filter(
        type=OuterRef('type'),
    ).values('type').values(
        inner_count=Count('galaxy', distinct=True),
    ))
)

Which would result in something along这会导致一些事情

SELECT
   star.*,
   (
       SELECT COUNT(DISTINCT inner_star.galaxy_id)
       FROM star inner_star
       WHERE inner_star.type = star.type
       GROUP BY inner_star.type
   ) galaxies_count
FROM star

Which likely perform poorly on some databases that don't materialize correlated subqueries (eg MySQL).这可能在一些没有实现相关子查询的数据库(例如 MySQL)上表现不佳。 In all cases make sure you index Star.type otherwise you'll get bad performance no matter what.在所有情况下,请确保对Star.type进行索引,否则Star.type都会获得糟糕的性能。 A composite index on ('type', 'galaxy') might be even better as it might allow you to perform index only scan (eg on PostgreSQL). ('type', 'galaxy')上的复合索引可能会更好,因为它可能允许您执行仅索引扫描(例如在 PostgreSQL 上)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM