Django 过滤精确的 m2m 对象

标签 django django-models m2m

假设我有一个团队模型,团队成员

所以

class Team(models.Model):
    team_member = models.ManyToManyField('Employee')

class Employee(models.Model):
    ....

假设我有一个员工 ID 列表,例如 team_members = [1001, 1003, 1004],我想找到 Team,它完全由这三个成员。

我不想要有 [1001, 1003, 1004, 1005] 的团队或有 [1001, 1003] 的团队。

只有团队 [1001, 1003, 1004]

这就是我现在正在做的:

teams = Team.objects.all()
for t in teams:
    if set([x.id for x in t.team_member.all()]) == set(team_members):
        team = t
if not team:
    team = Team.objects.create()
    team.team_member = team_members

但似乎有点笨手笨脚。有没有更简洁、嵌套循环更少的方法?

最佳答案

简短的回答

不,我不知道在代码外观方面有更简单的方法。

但是,您可以做一些事情来使您的代码更优雅,并可能更快。此外,可以在数据库中完成工作,尽管对于大型团队来说效率很低。

下面列出的 DB 选项与您提供的 for 循环一样笨手笨脚,但根据您的数据集、DB 等可能会更有效。

更长的答案:减少“笨手笨脚”的方法

这里有几个地方我要清理样式。

另外,根据我使用 Django 的经验,您构建的循环do 在大型数据集上往往会变得相当昂贵。如果您最终将 10,000 个团队加载到内存中,让 ORM 将它们转换为 Team 对象,然后迭代它们,您可能会看到一些明显的减速。

尝试速度和优雅的两件事:

  1. Team.values_list('team_members') 用于您的 in-python 过滤器循环,它会跳过 Django 将所有 SQL 数据组织到 Model 对象中的步骤。我发现这可以节省大量实例化对象的时间(有时大约节省一个数量级)。
  2. 整理您的 set() 调用。目前,您在每次迭代时都将 team_members 重新转换为 set(),此外,您还将 t.team_member 隐式转换为 TeamMember 对象(因为它们是从数据库中获取的),然后放入 idlist 中,然后放入 set。对于第一项,只需在前面制作一个 team_members_set = set(team_members) 并重用它。对于第二项,您可以执行 set(t.team_member.values_list('id', flat=True)) 这将跳过实例化 TeamMember 的最重的 ORM 步骤(根据数据集和 Django 的缓存,这可能与示例中的 O(n^2) 一样糟糕)。
  3. 使用Team.objects.all().iterator()不要一次将 Team 全部加载到内存中。如果您遇到内存问题,这会有所帮助。

但是对于任何性能优化,当然要使用真实或真实的数据测试您的性能,以确保您不会让事情变得更糟!

更长的答案:数据库选项

在尝试了各种Q() 操作和此处答案中列出的其他方法后,无济于事,我找到了 this answer by @Todor .

基本上您需要重复执行 filter(),每个 team_member 一个。最重要的是,您使用 Count 过滤器来确保您最终不会选择包含所需成员超集的 Team

desired_members = [1001, 1003, 1004]
initial_queryset = Team.objects.annotate(cnt=models.Count('team_members')).filter(cnt=len(desired_members))
matching_teams = reduce( # Can of course use a for loop if you prefer that to reduce()
    lambda queryset, member: queryset.filter(team_members=member),
    desired_members,
    initial_queryset
)

请注意,对于大型团队,生成的查询可能会出现性能问题,因为它会为您的每个 desired_members 执行一次 JOIN。最好避免这种情况,但我不知道在不更改数据结构的情况下在数据库中执行所有操作的另一种方法。我很想学习更好的方法,如果您最终进行了一些性能测试,我很想知道您学到了什么!

关于Django 过滤精确的 m2m 对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35804157/

相关文章:

通过 AJAX 将 Javascript POST 发送到 Django 根本无法正常工作

python - 如何测试 python 中的递归函数不会永远持续下去?

python - 连接这两个 Django 模型的最佳方式是什么

django - 如何使用自动完成灯创建依赖下拉列表

python - 如何覆盖模型上的 delete() 并让它仍然与相关的删除一起使用

Django EmbeddedModelField 在执行 PUT 请求时说 "This field may not be blank",尽管字段具有 "blank=True"

JavaScript 不读取多选中的文本

python - Django 查询详情 View

django - 如何从查询集中创建选择字段并在表单中实现它

python - 在 Django 中保存这种关系的最佳方法是什么?