如何正确查询ManyToManyField列表中的所有对象(或另一个ManyToManyField)?

时间:2009-12-03 18:33:05

标签: django django-queryset django-orm

我对构建Django查询的最佳方法感到困惑,该查询检查 all ManyToMany字段(或列表)的元素是否存在于另一个{{1 }} field。

作为一个例子,我有几个ManyToMany,可以拥有多个专业。人们也可以Person开始,但他们需要一个或多个Job才有资格启动。

Specialty

如果某人所有工作所需的专业,那么他们就可以 开始工作。所以,再举一个例子,我们有三个专长:

  • 编码
  • 歌唱
  • 跳舞

我有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') 需要唱歌和舞蹈专业。有歌唱和舞蹈专业的人可以开始,但另一个有编码和唱歌专业的人不能 - 因为工作需要一个可以唱歌和跳舞的人。

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

Job

这是因为使用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() 将返回与该人员的任何相匹配的作业,而不是全部。使用此查询,需要唱歌和跳舞的作业将出现在唱歌编码器中,这不是所需的输出。

我希望这个例子不会太复杂。我担心的原因是系统中的Specialties可能会更多,并且循环它们似乎不是实现这一目标的最佳方式。我想知道是否有人可以帮助这个痒!

2 个答案:

答案 0 :(得分:12)

另一个想法

好吧我想我应该把这个添加到另一个答案中,但是当我开始使用它时,它似乎是一个不同的方向哈哈

无需迭代:

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)

注意我不确切知道这有多快。你可能会对我的其他建议感觉更好。
另外:此代码未经测试

答案 1 :(得分:4)

我认为你应该考虑使用values_list来获得这个人的专长

替换:

[s.name for s in person.specialties]

使用:

person.specialties.values_list('name', flat=True)

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

您也可以通过过滤此人绝对无法执行的作业来提高速度:

所以替换:

jobs = Job.objects.all()

with(2个查询 - 适用于django 1.0 +)

person_specialties = person.specialties.values_list('id', flat=True)
jobs = Job.objects.filter(required_specialties__id__in=person_specialties)

或者(1个查询? - 适用于django1.1 +)

jobs = Job.objects.filter(required_specialties__in=person.specialties.all())

您也可以通过select_related()对您的工作/人员查询进行改进(因为他们有您正在使用的外键)