【问题标题】:Django ORM filter SUM different related objects on a loopDjango ORM在循环中过滤SUM不同的相关对象
【发布时间】:2020-12-15 19:05:59
【问题描述】:

我有以下型号:

    class Developer(models.Model):
        name = models.CharField(max_length=100)

    class Skill(models.Model):
        code = models.CharField(max_length=30)

    class Experience(models.Model):
        date_from = models.DateField(blank=True, null=True)
        date_to = models.DateField(blank=True, null=True)
        developer = models.ForeignKey(Developer, related_name='experience', 
        on_delete=models.CASCADE)
    
    class SkillExperience(models.Model):
        skill = models.ForeignKey(Skill, on_delete=models.CASCADE, related_name='skill_experience')
        experience = models.ForeignKey(Experience, on_delete=models.CASCADE, related_name='skill_experience')
        years_experience = models.IntegerField()

我知道,对于单个查询,要让拥有至少 5 年 Python 经验的开发人员,我可以这样做:

Developer.objects.filter(
    experience__skill_experience__skill__code='Python'
).annotate(
    total_years=Sum('experience__skill_experience__years_experience')
).filter(
    total_years__gte=5
)

但是,如果我处于技能循环中并且需要检查 OR,那么如何进行操作,所以假设循环这个数组:[{code: 'python', years_experience: 5}, {code: 'node' , years_experience: 2}] 我需要返回拥有 5 年 Python 经验或 2 年节点经验的开发人员?

【问题讨论】:

    标签: python django django-orm


    【解决方案1】:

    我认为最简单的方法是确定所有技能的总和,然后检查其中至少一项是否满足约束条件,因此:

    # since Django-2.0
    
    from django.db.models import Q, Sum
    
    # sample data
    data = [
        {code: 'python', years_experience: 5},
        {code: 'node', years_experience: 2}
    ]
    
    annotates = {
        f'total_years_{i}': Sum(
            'experience__skill_experience__years_experience',
            filter=Q(experience__skill_experience__skill__code=di['code'])
        ) for i, di in enumerate(data)
    }
    
    qs = Q(
        *[(f'total_years_{i}__gte', di['years_experience']) for i, di in enumerate(data)],
        _connector=Q.OR
    )
    
    developers = Developer.objects.annotate(
        **annotates
    ).filter(
        qs    
    )

    作为奖励,developers 中的Developer 对象将具有.total_years_0.total_years_1 等属性,用于包含该技能年数总和的列表中的每个技能。

    【讨论】:

    • 我在 ** 后得到一个“filter() 参数必须是映射,而不是 Q”错误。如果我尝试删除最后一个过滤器上的 **,我会得到错误的结果,对于“python 5 年 xp”的简单查询。它返回空。
    • 我还在测试边缘情况,但我认为应该是total_years_{i}__gte...
    • @Luc:抱歉确实有一些错字,你说得对,过滤应该是__gte
    猜你喜欢
    • 2018-11-18
    • 1970-01-01
    • 2022-06-12
    • 2017-07-02
    • 2010-09-20
    • 2019-02-14
    • 2012-11-29
    相关资源
    最近更新 更多