想象一下,我正在尝试计算某个公园中树木品种的数量,并且这些树木被分组在公园内的特定区域中。 一个问题是树木可以重新种植,所以我只想计算现有的树木和空位置(
removed IS NULL
表示当前种植的树木,LEFT
和RIGHT JOIN
表示尚未种植的位置)。 在浏览了大量 Django select_related
帖子和文档后,我不知道如何通过 Django 的查询 API 来做到这一点。 我有公园 ID,所以我将从 views.py
中的 Parks 模型开始执行此操作。
我在 Django 的查询 API 中遗漏了一些明显(或不明显)的东西吗?
以下 SQL 执行我想要的操作:
WITH p AS (
SELECT t.cultivar, l.id AS loc
FROM trees t
JOIN locations l ON t.location = l.id
JOIN fields d ON l.field = d.id
WHERE d.park = 'SOME_PARK_ID'
AND t.removed IS NULL
)
SELECT c.name, count(*)
FROM p
LEFT JOIN cultivars c ON p.cultivar = c.id
RIGHT JOIN locations l ON p.loc = l.id
GROUP BY name;
例如:
+--------+-------+
| name | count |
|--------+-------|
| <null> | 2 |
| HGG | 2 |
| BOX | 3 |
| GRV | 1 |
+--------+-------+
可能
views.py
:
class ParkDetail( DetailView ):
model = Parks
# Would like to get a count of cultivars here
def get_context_data( self, **kwargs ): # ... Lost here
qry = Parks.objects.select_related( 'tree__location__field' ).filter( tree.removed is None )
tree_count = qry.annotate( Count( ... ) )
models.py
的德语部分:
class Parks( models.Model ):
name = models.CharField( max_length = 64, blank = False )
class Fields( models.Model ):
park = models.ForeignKey( Parks, on_delete = models.CASCADE )
class Locations( models.Model ):
field = models.ForeignKey( Fields, on_delete = models.CASCADE )
class Cultivars( models.Model ):
name = models.CharField( max_length = 64, blank = False )
class Trees( models.Model ):
location = models.ForeignKey( Locations, on_delete = models.CASCADE )
cultivar = models.ForeignKey( Cultivars, on_delete = models.PROTECT )
planted = models.DateField( blank = False )
removed = models.DateField( blank = True, null = True )
我不明白你在这里需要什么
.select_related(…)
。您可以使用以下方法计算给定 Trees
未删除的 park_id
:
from django.db.models import Count
Cultivars.objects.filter(
tree__removed=None, tree_location__field__park_id=my_park_id
).annotate(tree_count=Count('tree'))
由这个
Cultivars
产生的QuerySet
将有一个名为.tree_count
的额外属性,其中未删除的Trees
的数量为my_park_id
为park_id
。
如果您还想检索未种植树木的
Cultivars
,我们可以使用:
from django.db.models import Case, Count, F, Value, When
Cultivars.objects.annotate(
tree_count=Count(
Case(
When(
tree__removed=None,
tree_location__field__park_id=my_park_id,
then=F('tree'),
),
default=Value(None),
)
)
)