有没有更简单的方法来显示我的对象类别?

时间:2012-06-28 01:42:53

标签: python django django-templates django-views

我有两个这样的模型:

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

3 个答案:

答案 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个对象。