我有两个这样的模型:
class ClassA(models.Model):
ida = models.AutoField(primary_key=True)
classb = models.ForeignKey(ClassB)
name = models.CharField(max_length=765)
class ClassB(models.Model):
idb = models.AutoField(primary_key=True)
name = models.CharField(max_length=765)
我想显示在ClassA
标题下分组的所有ClassB
个对象的列表。这是我的观点:
def show_class_a_objects(request):
class_b_objects = ClassB.objects.all().order_by('name')
class_a_objects = ClassA.objects.all().order_by('name')
return render_to_response('show_objects.html', {
'class_a_objects': class_a_objects, 'class_a_objects': class_a_objects,
})
我的show_objects.html
文件如下所示:
{% extends "base_show.html" %}
{% block content %}
</p>
<table>
<th align="left">Name</th>
{% for b in class_b_objects %}
<tr>
<td>{{b.name}}</td>
</tr>
{% for a in class_a_objects %}
{% ifequal a.classb b %}
<tr>
<td><a href="{{ a.get_absolute_url }}">{{a.name}}</a></td>
</tr>
{% endifequal %}
{% endfor %}
{% endfor %}
</table>
</br>
{% endblock %}
有没有更好的方法来实现这一点,而不是使用两个嵌套的for循环?
答案 0 :(得分:1)
def show_class_a_objects(request):
class_b_objects = ClassB.objects.all().order_by('name')
class_a_objects = ClassA.objects.all().order_by('name')
您不需要像这样查询ClassA对象来实现所需的分组。在ClassA上创建ForeignKey时 - &gt; ClassB,你的ClassB将包含一个关系,它将返回所有具有ClassB作为ForeignKey的ClassA对象:
查看强>
def show_class_a_objects(request):
class_b_objects = ClassB.objects.all().order_by('name')
return render_to_response('show_objects.html', {
'class_b_objects': class_b_objects,
})
<强>模板强>
{% for b in class_b_objects %}
<tr>
<td>{{b.name}}</td>
</tr>
{% for a in b.classa_set.all %}
<tr>
<td><a href="{{ a.get_absolute_url }}">{{a.name}}</a></td>
</tr>
{% endfor %}
{% endfor %}
你仍然需要一个嵌套的for循环,但是你不再需要循环遍历每个其他类的整个范围并进行比较以找到正确的循环。
如果你想要更多控制,你应该再次将它移到你的视图中并预建dict:
查看强>
def show_class_a_objects(request):
class_b = ClassB.objects.all().order_by('name')
class_b_objects = [(b, b.classa_set.all().order_by('name')) for b in class_b]
return render_to_response('show_objects.html', {
'class_b_objects': class_b_objects
})
<强>模板强>
{% for b, a_list in class_b_objects %}
<tr>
<td>{{b.name}}</td>
</tr>
{% for a in a_list %}
<tr>
<td><a href="{{ a.get_absolute_url }}">{{a.name}}</a></td>
</tr>
{% endfor %}
{% endfor %}
更新:减少查询次数
根据评论,我想解决将数据库查询量从原来的2增加到1 + num_of_classb_objects
的问题。这是因为当原始ClassB查询为1时,每个b.classa_set.all()
关系调用都是另一个获取相关ClassA实例的查询。
如果您想尝试测试替代方案,可以在efficient reverse lookups
上查看此博文。示例(改编自您的型号名称的链接):
qs = ClassB.objects.all()
obj_dict = dict([(obj.id, obj) for obj in qs])
objects = ClassA.objects.filter(classb__in=qs)
relation_dict = {}
for obj in objects:
relation_dict.setdefault(obj.classb_id, []).append(obj)
for id, related_items in relation_dict.items():
obj_dict[id]._related_items = related_items
此外,如果您正在运行django&gt; = 1.4,则此功能可能是prefetch_related
形式的正式版。
我没有对此进行测试,但我想将其作为一些内容供您查看,如果n+1
查询不被您接受。
答案 1 :(得分:1)
为避免通过反向关系访问每组A
而生成的大量查询,您可以执行以下操作:
>>> from itertools import groupby
>>> alla = ClassA.objects.filter().order_by('classb').select_related('classb')
>>> groups = []
>>> for key,group in groupby(alla, lambda a: a.classb_id):
... groups.append(list(group))
现在你有一个可迭代的迭代!我们专注于具有外键的类只是因为您只需要执行一个查询(这更复杂,但从长远来看可以节省您的时间)。上面发生的是,我们按A
排序B
,然后将A
的每个列表保存到自己的列表/可迭代中。由于A
能够访问B
,因此您无需担心迭代B
。
>>> for g in groups:
... print g[0].classb # this is your grouping/header
... print g # this is a list of your classa instances
如果你想减少通过生成器的迭代,你可以获得一点点发烧友,但我发现通过专注于ForeignKey
的模型,你会得到更好的SQL性能,代价是更复杂您视图中的代码。复杂性(IMO)可以忽略不计,因为大量SQL查询可能会导致应用程序死亡。
答案 2 :(得分:0)
也许您可以在class B
模型中ManyToManyField()
在每个class A
中包含多个object B
个对象。这样您只需调用该字段即可获得具有相同class A
值的所有class B
个对象。