rla*_*nte 16 django django-orm django-queryset
我对构建Django查询的最佳方法感到困惑,该查询检查字段(或列表)的所有元素ManyToMany是否存在于另一个ManyToMany字段中.
举个例子,我有几个Person,可以有多个专业.也有Jobs表示人们就可以开始,但他们需要一个或多个Specialtys到有资格被启动.
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')
Run Code Online (Sandbox Code Playgroud)
一个人只有拥有工作所需的所有专业,才能开始工作.所以,再举一个例子,我们有三个专长:
我有一个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()
Run Code Online (Sandbox Code Playgroud)
这是因为使用Job.objects.filter(specialty__in=person.specialties.all())将返回与任何人的专业相匹配的工作,而不是所有工作.使用此查询,需要唱歌和跳舞的作业将出现在唱歌编码器中,这不是所需的输出.
我希望这个例子不会太复杂.我担心的原因是系统中的Specialties可能会更多,并且循环它们似乎不是实现这一目标的最佳方式.我想知道是否有人可以帮助这个痒!
Jia*_*aro 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)
Run Code Online (Sandbox Code Playgroud)
注意:我不知道这到底有多快.我可能会对其他建议感觉更好.
另外:此代码未经测试
我认为您应该考虑使用values_list来获取此人的特长
代替:
[s.name for s in person.specialties]
Run Code Online (Sandbox Code Playgroud)
和:
person.specialties.values_list('name', flat=True)
Run Code Online (Sandbox Code Playgroud)
这将为您提供一个简单的列表(即 ['spec1', 'spec2', ...]),您可以再次使用它。并且 bg 中使用的 sql 查询也会更快,因为它只会选择 'name' 而不是执行 aselect *来填充 ORM 对象
您还可以通过过滤该人肯定无法执行的工作来提高速度:
所以替换:
jobs = Job.objects.all()
Run Code Online (Sandbox Code Playgroud)
with (2 个查询 - 适用于 django 1.0+)
person_specialties = person.specialties.values_list('id', flat=True)
jobs = Job.objects.filter(required_specialties__id__in=person_specialties)
Run Code Online (Sandbox Code Playgroud)
或使用(1 个查询? - 适用于 django1.1+)
jobs = Job.objects.filter(required_specialties__in=person.specialties.all())
Run Code Online (Sandbox Code Playgroud)
您还可以通过在您的工作/人员查询中使用select_related()来获得改进(因为它们有您正在使用的外键)