Django 如何正确查询列表(或其他ManyToManyField)中所有对象的ManyToManyField?

Django 如何正确查询列表(或其他ManyToManyField)中所有对象的ManyToManyField?,django,django-queryset,django-orm,Django,Django Queryset,Django Orm,我对构建Django查询的最佳方法感到困惑,该查询检查ManyToMany字段(或列表)的所有元素是否都存在于另一个ManyToMany字段中 例如,我有几个人s,他们可以拥有多个专业。人们也可以开始工作,但他们需要一个或多个专业才能开始工作 class Person(models.Model): name = models.CharField() specialties = models.ManyToManyField('Specialty') class Specialty

我对构建Django查询的最佳方法感到困惑,该查询检查
ManyToMany
字段(或列表)的所有元素是否都存在于另一个
ManyToMany
字段中

例如,我有几个
s,他们可以拥有多个专业。人们也可以开始工作,但他们需要一个或多个专业才能开始工作

class Person(models.Model):
    name = models.CharField()
    specialties = models.ManyToManyField('Specialty')

class Specialty(models.Model):
    name = models.CharField()

class Job(models.Model):
    required_specialties = models.ManyToManyField('Specialty')
一个人只有具备工作所需的所有专业才能开始工作。因此,再次举个例子,我们有三个特色:

  • 编码
  • 歌唱
  • 跳舞
我有一份工作,需要唱歌和跳舞的专业技能。一个有唱歌和跳舞专长的人可以开始,但另一个有编码和唱歌专长的人不能——因为这项工作需要一个既会唱歌又会跳舞的人

所以,现在我需要一种方法来找到一个人可以承担的所有工作。这是我解决问题的方法,但我相信还有更优雅的方法:

def jobs_that_person_can_start(person):
    # we start with all jobs
    jobs = Job.objects.all()
    # find all specialties that this person does not have
    specialties_not_in_person = Specialty.objects.exclude(name__in=[s.name for s in person.specialties])
    # and exclude jobs that require them
    for s in specialties_not_in_person:
        jobs = jobs.exclude(specialty=s)
    # the ones left should fill the criteria
    return jobs.distinct()
这是因为使用
Job.objects.filter(speciality\uu in=person.specialties.all())
将返回与人员的任何专业相匹配的作业,而不是所有专业。使用此查询,需要唱歌和跳舞的作业将为唱歌编码器显示,这不是所需的输出


我希望这个例子不要太复杂。我之所以担心这一点,是因为系统中的专业可能会多得多,而围绕它们进行循环似乎不是实现这一点的最佳方式。我想知道是否有人能帮我搔痒

我想你应该考虑一下如何使用来获得这个人的专长

替换:

[s.name for s in person.specialties]
与:

这将为您提供一个简单的列表(即[spec1]、[spec2]、…]),您可以再次使用它。bg中使用的sql查询也会更快,因为它只会选择“name”,而不是执行
select*
来填充ORM对象

您还可以通过过滤此人肯定无法执行的作业来提高速度:

因此,请替换:

jobs = Job.objects.all()
带(2个查询-适用于django 1.0+)

或使用(1个查询?——适用于django1.1+)

您还可以通过在您的工作/人员查询中使用(因为您使用的是外键)来获得改进。

另一个想法 好吧,我想我应该在另一个答案中加上这个,但是当我开始的时候,它似乎将是一个不同的方向哈哈

无需迭代:

person_specialties = person.specialties.values_list('pk', flat=True)

non_specialties = Specialties.objects.exclude(pk__in=person_specialties)

jobs = Job.objects.exclude(required_specialties__in=non_specialties)
注意:我不知道这到底有多快。我的其他建议可能会让你过得更好。

另外:这段代码未经Django 1.1测试

,您可以简单地执行jobs=Job.objects.filter(必选的\u specialties\u in=person.specialties.all())@Evgeny我想了一下,它是否执行与我这里相同的查询?我不确定哇--非常感谢!遗憾的是,没有一种“神奇”的方法可以做到这一点,但你的建议是这里的救命稻草。@Evgeny根据django文档,一些数据库后端(如mysql)使用子查询方法的速度不是很快。我不认为这种方法可以过滤只有“全部”质量的记录。这段代码工作得很好,明显比我的尝试快。非常感谢你的建议,我在这里学到了很多。我将这个答案标记为已被接受,因为这正是我的问题所在;尽管您的另一个答案暗示了许多其他性能改进。
person_specialties = person.specialties.values_list('id', flat=True)
jobs = Job.objects.filter(required_specialties__id__in=person_specialties)
jobs = Job.objects.filter(required_specialties__in=person.specialties.all())
person_specialties = person.specialties.values_list('pk', flat=True)

non_specialties = Specialties.objects.exclude(pk__in=person_specialties)

jobs = Job.objects.exclude(required_specialties__in=non_specialties)