Django中如何统计关联Company的在线Ad数量?
问题描述
我开发了一个返回所有Company对象的视图,希望在companySearch.html模板中统计每个Company关联的、online字段为True的Ad数量。但当前模板会统计所有关联的Ad(包括online为False的),我期望实现类似{{ o.ad_set.count where o.ad.online == True }}的逻辑。
相关代码如下:
views.py
def companySearch(request): obj = Company.objects.all() paginator = Paginator(obj, PAGINATION_NUMBER) page_number = request.POST.get('page') page_obj = paginator.get_page(page_number) context = {'obj': page_obj} return render(request, 'core/companySearch.html', context)
models.py
class Company(models.Model): id = models.BigAutoField(primary_key=True) name = models.CharField(max_length=64, blank=False, null=False, unique=True) class Ad(models.Model): id = models.BigAutoField(primary_key=True) name = models.CharField(max_length=128, blank=False, null=False) company = models.ForeignKey(Company, on_delete=models.CASCADE) online = models.BooleanField(default=False)
companySearch.html
{% for o in obj %} <div>{{ o.ad_set.count }} open position{{ o.ad_set.count|pluralize:',s' }}</div> {% endfor %}
解决方案
方法1:模型中添加属性(简单易实现)
给Company模型新增一个属性,专门返回在线广告的数量:
class Company(models.Model): id = models.BigAutoField(primary_key=True) name = models.CharField(max_length=64, blank=False, null=False, unique=True) @property def online_ad_count(self): return self.ad_set.filter(online=True).count()
模板中直接调用该属性:
{% for o in obj %} <div>{{ o.online_ad_count }} open position{{ o.online_ad_count|pluralize:',s' }}</div> {% endfor %}
⚠️ 注意:这种方式会为每个Company单独发起数据库查询,数据量大时可能影响性能。
方法2:视图用annotate预计算(性能最优)
在查询Company时,通过annotate提前统计好每个公司的在线广告数量,只需要一次数据库查询:
from django.db.models import Count, Q def companySearch(request): obj = Company.objects.annotate( online_ad_count=Count('ad', filter=Q(ad__online=True)) ) paginator = Paginator(obj, PAGINATION_NUMBER) page_number = request.POST.get('page') page_obj = paginator.get_page(page_number) context = {'obj': page_obj} return render(request, 'core/companySearch.html', context)
模板中同样使用o.online_ad_count即可,和方法1的模板代码一致。
方法3:模板直接过滤(不推荐)
虽然Django模板支持直接过滤,但会引发N+1查询问题,性能较差,不建议使用:
{% for o in obj %} <div>{{ o.ad_set.filter(online=True).count }} open position{{ o.ad_set.filter(online=True).count|pluralize:',s' }}</div> {% endfor %}
内容的提问来源于stack exchange,提问作者GCMeccariello
相关产品推荐
相关产品推荐

