首页 > 解决方案 > Django:聚合多对多值的查询集

问题描述

我有 MyUser 模型,其中包含 ForeignKey 和 ManyToMany 相关领域的城市技能

帐户/模型.py

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

工作/模型.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 值聚合为一组的查询集呢?

标签: pythondjangodjango-orm

解决方案


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,
    })

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


推荐阅读