如何将Django双循环计算逻辑转换为Queryset查询?
Django QuerySet 优化实现方案
模型定义
from django_better_admin_arrayfield.models.fields import ArrayField class BuildItem(models.Model): base = models.ForeignKey(Item, on_delete=models.CASCADE) level = models.IntegerField(default=1) mandatory_skills = ArrayField(models.IntegerField(null=True, blank=True), default=list) class Item(BaseModel): name = models.CharField(max_length=100) class Skill(BaseModel): item = models.ForeignKey(Item, on_delete=models.CASCADE, blank=True, null=True, related_name="skills") effect = models.IntegerField(null=True, blank=True) value = models.IntegerField(null=True, blank=True)
需求说明
计算满足以下所有条件的 skill.value * builditem.level 的总和:
- Skill 的主键存在于对应 BuildItem 的
mandatory_skills数组中 - Skill 的
effect字段等于 1 - Skill 的
value字段小于 2
现有双循环实现
array_life = [] for builditem in BuildItem.objects.all(): for skill in builditem.base.skills.all(): if skill.pk in builditem.mandatory_skills: if skill.effect == 1 and skill.value < 2: array_life.append(skill.value * builditem.level) sum_life = sum(array_life)
优化后的 QuerySet 实现
利用 Django ORM 的聚合、注解和字段表达式功能,将上述逻辑转化为单次数据库查询,大幅提升性能:
from django.db.models import Sum, F, Q # 计算符合条件的乘积总和 sum_life = BuildItem.objects.filter( # 过滤满足effect和value条件的关联Skill base__skills__effect=1, base__skills__value__lt=2 ).filter( # 确保当前Skill的主键在BuildItem的mandatory_skills数组中 Q(mandatory_skills__contains=[F('base__skills__pk')]) ).annotate( # 计算单个Skill与对应BuildItem的乘积 product=F('base__skills__value') * F('level') ).aggregate( # 聚合所有乘积的总和 total=Sum('product') )['total'] or 0
代码说明
- 过滤条件:先筛选出关联了符合
effect=1、value<2的Skill的BuildItem,再进一步过滤这些Skill的主键存在于BuildItem的mandatory_skills数组中。 - 注解乘积:通过
annotate和F()表达式,直接在数据库层面计算skill.value * builditem.level,避免内存中计算。 - 聚合总和:用
aggregate和Sum聚合所有符合条件的乘积结果,最后用or 0处理无符合条件记录时返回None的情况。
内容的提问来源于stack exchange,提问作者Hippolyte BRINGER
相关产品推荐
相关产品推荐

