1

我有两个这样的模型:

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 循环?

4

3 回答 3

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 -> ClassB 上创建 ForeignKey 时,您的 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 循环,但您不再需要遍历每个其他类的整个范围并进行比较以找到正确的类。

如果你想要更多的控制,你应该再次将它移动到你的视图中并预先构建字典:

看法

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 实例。

如果您想尝试并测试替代方案,您可以查看这篇关于高效反向查找的博客文章

示例(改编自您的型号名称的链接):

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 >= 1.4,则此功能应该是官方的prefetch_related.

我没有对此进行测试,但我想将其呈现为您可以查看的内容,如果n+1您不接受查询。

于 2012-06-28T01:56:47.157 回答
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))

现在你有了一个可迭代的迭代!我们专注于具有外键的类仅仅是因为您只需要执行一个查询(这更复杂,但从长远来看可以节省您的时间)。上面发生的事情是,我们正在对列表进行排序AB然后将每个列表保存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 查询可能会导致您的应用程序死亡。

于 2012-06-28T03:22:43.743 回答
0

也许你可以ManyToManyField()在你的class B模型中包含多个class A对象object B。这样您只需调用该字段即可获取class A具有相同值的所有对象class B

于 2012-06-28T01:54:58.023 回答