如何在Django ORM中为LandChangeHistory查询集关联区域土地数?
Django ORM 给LandChangeHistory添加区域土地总数注解
问题背景
现有Django模型:
class Regions(models.Model): name = models.CharField(max_length=255, unique=True) class Owners(models.Model): name = models.CharField(max_length=255, null=False, unique=True) url = models.URLField(null=True) class Lands(models.Model): region = models.ForeignKey(Regions, on_delete=models.CASCADE) owner = models.ForeignKey(Owners, on_delete=models.PROTECT, null=True) description = models.TextField(max_length=2000, null=True) class LandChangeHistory(models.Model): land = models.ForeignKey(Lands, on_delete=models.CASCADE, null=False, related_name='lands') price = models.IntegerField() size = models.IntegerField() date_added = models.DateField(auto_now_add=True)
已实现统计各区域土地数量的查询集:
lands_in_region = Lands.objects.values('region__name').annotate(count=Count('region_id'))
返回示例:{'region__name': 'New York', 'count': 3}, {'region__name': 'Chicago', 'count': 2}
需要给LandChangeHistory的每条记录添加所属区域的土地总数注解,但当前代码中count始终为1:
f = LandFilter(request.GET, queryset=LandChangeHistory.objects.all() .select_related('land', 'land__region', 'land__owner') .annotate(usd_per_size=ExpressionWrapper(F('price') * 1.0 / F('size'), output_field=FloatField(max_length=3))) .annotate(count=Count('land__region_id')) )
期望输出:
| 土地标识 | 价格 | 面积 | 区域 | 区域土地总数 |
|---|---|---|---|---|
| land1 | 100$ | 100m2 | New York | 2 |
| land2 | 105$ | 105m2 | New York | 2 |
| land3 | 102$ | 102m2 | Chicago | 1 |
要求用Django ORM实现,不使用原生SQL关联表。
解决方案
问题根源
直接用Count('land__region_id')时,Django只会统计当前LandChangeHistory关联的单条land对应的region_id出现次数,所以每条记录的count都是1,无法得到区域的总土地数。
正确实现代码
使用Subquery和OuterRef关联区域土地总数的统计结果:
from django.db.models import Subquery, OuterRef, Count, ExpressionWrapper, FloatField # 定义子查询:统计指定区域下的土地总数 region_land_total = Lands.objects.filter( region=OuterRef('land__region') # 关联外层查询的land所属区域 ).values('region').annotate( total=Count('id') ).values('total') # 只提取统计结果 # 构建最终查询集 queryset = LandChangeHistory.objects.all() .select_related('land', 'land__region', 'land__owner') .annotate( usd_per_size=ExpressionWrapper(F('price') * 1.0 / F('size'), output_field=FloatField()) ) .annotate( count=Subquery(region_land_total) # 将子查询结果作为注解字段 ) f = LandFilter(request.GET, queryset=queryset)
代码说明
- 子查询
region_land_total会针对每条LandChangeHistory记录对应的land所属区域,计算该区域下所有Lands的总数。 OuterRef('land__region')用来关联外层查询中当前LandChangeHistory记录的land字段对应的region,确保每个历史记录都能匹配到对应区域的统计值。- 最后通过
Subquery把统计结果注入到LandChangeHistory的查询集中,作为count字段返回。
这样就能得到期望的输出,每条历史记录都会显示所属区域的土地总数。
内容的提问来源于stack exchange,提问作者Danza
相关产品推荐
相关产品推荐

