使用 prefetch_related 对多个外键进行 Django 查询集
Django queryset over multiple foreign keys using prefetch_related
这是我的模型:
class Owner():
last_name = models.CharField(max_length=30)
...
class Species():
species_code = models.CharField(max_length=10)
...
class Pet():
client = models.ForeignKey(Owner, related_name='pet_fk')
species = models.ForeignKey(Species)
....
我想列出所有主人和他们的宠物。有些主人没有宠物,有些则有很多。
如果找到宠物,我想为该动物的对象注释一个额外的 'temporary' 字段 css_species_class
。如果宠物模型的 species_code 为 'CANINE',则此字段将 return 'dog',如果 'EQUINE' 等 'horse' 等
由于站点是多语言的,因此需要 'temporary' 字段,并且需要 css_species_class
值才能在模板中引入适当的字形图标。我不能直接使用存储的值,所以这就是为什么我需要插入一个特定的值来匹配字形期望的值。
类似于:
Owner: John Smith
Pet: Saag (css_species_class='dog')
Pet: Brinjal (css_species_class='cat')
Pet: Baji (css_species_class='dog')
Owner: Sue Smith
Pet: none
Owner: Clare Smith
Pet: Aloo (css_species_class='horse')
我的模板是这样的:
{% for owner in owners %}
<tr>
<td>{{ owner.first_name }} {{ owner.last_name }}</td>
<td> <!-- loop over pet objects -->
{% for pet in owner.pet_fk.all %}
<div>
....
<span class="glyphicons glyphicons-{{ pet.css_species_class }}"></span>
....
</div>
{% endfor pet %}
</td>
</tr>
{% endfor %}
所以,这是我的第一个解决方案:
class OwnerListView(ListView):
template_name = 'visitors/owner_list.html'
context_object_name = 'owners'
paginate_by = 50
def get_queryset(self):
owners_with_pets = Owner.objects.filter(pet_fk__isnull=False).prefetch_related('pet_fk').distinct()
# logic goes here to loop over pets
# and assign 'css_species_class' temp field
owners_without_pets = Owner.objects.filter(pet_fk__isnull=True).prefetch_related('pet_fk').distinct()
然后'merge'两个查询集在一起:
result_list = sorted(
chain(owners_with_pets, owners_without_pets),
key=attrgetter('last_name'))
return result_list
这 'works' 适用于少数所有者,但如果我用真实数字(大约 4,000)进行测试,我会得到 'too many sql variable' 错误。
我最初尝试在单个查询中执行此操作(在决定将其分成两个查询之前),但对于大量客户来说,这也失败了。
有人可以就如何最好地解决这个问题给我一些指导吗?非常感谢。
试试这个代码,未经测试,但我认为它可以工作。
class Owner():
last_name = models.CharField(max_length=30)
...
class Species():
species_code = models.CharField(max_length=10)
...
class Pet():
# related_name is used for backward relation
# this will end up as owner."related_name" --> owner.pets
client = models.ForeignKey(Owner, related_name='pets')
species = models.ForeignKey(Species)
...
@property
def css_species_class(self):
# this could be anything you want eg: css_scecies_class
return self.species.species_code
class OwnerListView(ListView):
template_name = 'visitors/owner_list.html'
context_object_name = 'owners'
paginate_by = 50
def get_queryset(self):
# no need to check if onwner instance has pets if you chain them both back
return Owner.objects.prefetch_related('pets').all().distinct()
{% for owner in owners %}
<tr>
<td>{{ owner.first_name }} {{ owner.last_name }}</td>
<td> <!-- loop over pet objects -->
{% for pet in owner.pets %}
<div>
....
<!-- now we can access pet.css_species_class directly because we made it a property of pet class -->
<span class="glyphicons glyphicons-{{ pet.css_species_class }}"></span>
....
</div>
{% endfor pet %}
</td>
</tr>
{% endfor %}
这是我的模型:
class Owner():
last_name = models.CharField(max_length=30)
...
class Species():
species_code = models.CharField(max_length=10)
...
class Pet():
client = models.ForeignKey(Owner, related_name='pet_fk')
species = models.ForeignKey(Species)
....
我想列出所有主人和他们的宠物。有些主人没有宠物,有些则有很多。
如果找到宠物,我想为该动物的对象注释一个额外的 'temporary' 字段 css_species_class
。如果宠物模型的 species_code 为 'CANINE',则此字段将 return 'dog',如果 'EQUINE' 等 'horse' 等
由于站点是多语言的,因此需要 'temporary' 字段,并且需要 css_species_class
值才能在模板中引入适当的字形图标。我不能直接使用存储的值,所以这就是为什么我需要插入一个特定的值来匹配字形期望的值。
类似于:
Owner: John Smith
Pet: Saag (css_species_class='dog')
Pet: Brinjal (css_species_class='cat')
Pet: Baji (css_species_class='dog')
Owner: Sue Smith
Pet: none
Owner: Clare Smith
Pet: Aloo (css_species_class='horse')
我的模板是这样的:
{% for owner in owners %}
<tr>
<td>{{ owner.first_name }} {{ owner.last_name }}</td>
<td> <!-- loop over pet objects -->
{% for pet in owner.pet_fk.all %}
<div>
....
<span class="glyphicons glyphicons-{{ pet.css_species_class }}"></span>
....
</div>
{% endfor pet %}
</td>
</tr>
{% endfor %}
所以,这是我的第一个解决方案:
class OwnerListView(ListView):
template_name = 'visitors/owner_list.html'
context_object_name = 'owners'
paginate_by = 50
def get_queryset(self):
owners_with_pets = Owner.objects.filter(pet_fk__isnull=False).prefetch_related('pet_fk').distinct()
# logic goes here to loop over pets
# and assign 'css_species_class' temp field
owners_without_pets = Owner.objects.filter(pet_fk__isnull=True).prefetch_related('pet_fk').distinct()
然后'merge'两个查询集在一起:
result_list = sorted(
chain(owners_with_pets, owners_without_pets),
key=attrgetter('last_name'))
return result_list
这 'works' 适用于少数所有者,但如果我用真实数字(大约 4,000)进行测试,我会得到 'too many sql variable' 错误。
我最初尝试在单个查询中执行此操作(在决定将其分成两个查询之前),但对于大量客户来说,这也失败了。
有人可以就如何最好地解决这个问题给我一些指导吗?非常感谢。
试试这个代码,未经测试,但我认为它可以工作。
class Owner():
last_name = models.CharField(max_length=30)
...
class Species():
species_code = models.CharField(max_length=10)
...
class Pet():
# related_name is used for backward relation
# this will end up as owner."related_name" --> owner.pets
client = models.ForeignKey(Owner, related_name='pets')
species = models.ForeignKey(Species)
...
@property
def css_species_class(self):
# this could be anything you want eg: css_scecies_class
return self.species.species_code
class OwnerListView(ListView):
template_name = 'visitors/owner_list.html'
context_object_name = 'owners'
paginate_by = 50
def get_queryset(self):
# no need to check if onwner instance has pets if you chain them both back
return Owner.objects.prefetch_related('pets').all().distinct()
{% for owner in owners %}
<tr>
<td>{{ owner.first_name }} {{ owner.last_name }}</td>
<td> <!-- loop over pet objects -->
{% for pet in owner.pets %}
<div>
....
<!-- now we can access pet.css_species_class directly because we made it a property of pet class -->
<span class="glyphicons glyphicons-{{ pet.css_species_class }}"></span>
....
</div>
{% endfor pet %}
</td>
</tr>
{% endfor %}