使用ORM这不应该太难;使用原始SQL非常容易。
我有一些模特:
class Actor(models.Model):
last_name = models.CharField(max_length=64)
specialty = models.ManyToManyField(Specialty, blank=True)
allergy = models.ManyToManyField(Allergy, blank=True)
#.... TONS MORE M2Ms HERE ....#
class Character(models.Model):
actor = models.ForeignKey(Actor)
name = models.CharField(max_length=64, null=True)
我认为这是正确的建模,因为一个演员扮演很多角色,但在大多数情况下,情况恰恰相反,例如:
m = Actor(last_name='Palin')
d = Character(actor=m, name='Dennis')
r = Character(actor=m, name='Right Head')
如果我想显示所有演员的列表,并获得他们所有的m2m关系,那很容易:
actors = Actor.objects.all().prefetch_related(specialty, allergy, ...)
根据用户输入,可以将一系列非常复杂的过滤器应用于上述查询集,例如actors allergic to peanuts but not allergic to filth
。
现在,如果我想获得相同的查询集,按不同的字符名称排序,我知道的唯一方法是查询字符,选择相关的actor,并预取相关的m2ms。
不幸的是,这需要我重新编写所有过滤器。因为allergy__name='peanuts'
必须成为actor__allergy__name='peanuts'
。
答案是什么?我如何以 DRY 方式提供“按演员排序”和“按字符排序”?
答案 0 :(得分:0)
如果您不想重写Actor过滤器,那么如何使用Actor QS来约束Character过滤器呢?例如:
actors = Actor.objects.filter(allergy__name='peanuts')
chars = Character.objects.filter(actor__in=actors)
然后您可以按照自己的意愿订购chars
。
编辑:我认为,我发现了您遇到的问题。您希望使用与Actor相关的其他记录预取字符。这是select_related()ex:
的一个很好的用例actors = Actor.objects.filter(allergy__name='peanuts').select_related('character_set').prefetch_related()
这应该会产生所有匹配的Actors以及结果集中包含的相关Character记录,允许您对Character name进行排序,而不是每次都访问数据库。最后prefetch_related()
会带来所有M2M记录,假设您也需要这些记录。
另一个编辑:从这里开始,有许多方法可以重新排序或重新组织结果数据。将此表示为字符记录(使用预加载的Actors)的一种可能方法可能是:
from itertools import chain
by_chars = chain(*[a.character_set.all() for a in actors])
或者,如果它更具可读性/可维护性:
by_chars = []
for a in actors:
for c in a.character_set.all():
by_chars.append(c)
由于您已经预先加载了所有内容,因此结果集的其他排序和分组不应该需要额外的数据库访问。