【问题标题】:Django: Queryset aggregating ManyToMany valuesDjango:聚合多对多值的查询集
【发布时间】:2020-11-08 13:07:22
【问题描述】:

我有 MyUser 模型,其中包含 ForeignKey 和 ManyToMany 相关字段 cityskills

accounts/models.py

class MyUser(AbstractBaseUser):
    email = models.EmailField()
    city = models.ForeignKey('jobs.City')
    skills = models.ManyToManyField('jobs.Skill')

jobs/models.py

class Skill(models.Model):
    name = models.CharField()
class City(models.Model):
    name = models.CharField()

我需要创建一个以这种格式返回数据的查询集:

{'email': 'some@email.com', 'city': 'London', 'skills': ['Python', 'Java']},
{'email': 'another@email.com', 'city': 'Berlin', 'skills': ['JavaScript', 'C#', 'Python']},
...

MyUser.objects.values('email', 'city__name', 'skills__name') 返回我需要的所有数据,但 ManytoMany 值分别返回,重复其他条目:

{'email': 'some@email.com', 'city': 'London', 'skills': 'Python'},
{'email': 'some@email.com', 'city': 'London', 'skills': 'Java'},
{'email': 'another@email.com', 'city': 'Berlin', 'skills': 'JavaScript'},
{'email': 'another@email.com', 'city': 'Berlin', 'skills': 'C#'},
{'email': 'another@email.com', 'city': 'Berlin', 'skills': 'Python'},
...

那么如何创建一个将 ManyToMany 值聚合为一组的查询集?

【问题讨论】:

    标签: python django django-orm


    【解决方案1】:

    https://docs.djangoproject.com/en/3.1/ref/models/querysets/#values

    Django 文档对此案例提出警告

    警告:因为 ManyToManyField 属性和反向关系可以有多个相关行,包括这些行会对结果集的大小产生乘数效应。如果您在 values() 查询中包含多个此类字段,这一点尤其明显,在这种情况下,将返回所有可能的组合。

    我已经搜索了替代方法,例如使用序列化程序,但我认为唯一的方法是遍历您的查询集并使用您想要的数据创建您自己的字典列表。

    我建议为您的模型添加自定义属性

    class MyUser(models.Model):
        email = models.EmailField()
        city = models.ForeignKey(City, on_delete=models.PROTECT)
        skills = models.ManyToManyField(Skill)
    
        @property
        def get_skills(self):
            skills = self.skills.all()
            skills_list = []
            for skill in skills:
                skills_list.append(skill.name)
    
            return skills_list
    

    现在更容易获得技能属性的名称列表

    users_list = []
    for my_user in MyUser.objects.all():
        users_list.append({
            'email': my_user.email,
            'city': my_user.city.name,
            'skills': my_user.get_skills,
        })
    

    也许这不是您正在寻找的解决方案,也许这不是最好的方法,无论如何我希望这个答案会对您有所帮助。

    【讨论】:

    • 感谢回复,我也想过循环解决方案。但是不需要添加get_skills 方法,因为my_user.skills.values_list('name', flat=True) 会返回相同的结果。
    猜你喜欢
    • 2018-02-23
    • 1970-01-01
    • 2017-04-21
    • 2013-08-22
    • 2021-11-14
    • 2011-02-19
    • 2011-04-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多