Django - 迭代一个查询集会产生数百个查询

时间:2015-10-22 10:13:23

标签: python django django-views

我有一个模型,其中定义了一个“变量模板”,并附加到数据库中的每个位置模型,生成这些变量的二维实例数组。

class Location(models.Model):
    name = models.CharField(max_length=4, blank=False, unique=True)

class LocationVariableDefinition(models.Model):
    name = models.CharField(max_length=32, blank=False, unique=True)
    type = models.CharField(max_length=60, default="text")
    default_value = models.CharField(max_length=250, blank=True, default="[UNSET]")

class LocationBasedVariable(models.Model):
    location = models.ForeignKey(Location)
    definition = models.ForeignKey(LocationVariableDefinition, default=None)

    value = models.CharField(max_length=250, blank=True)

要编辑这些变量,我有一个页面,其中包含表格,侧面有变量名称,顶部有位置。我添加了一个新的SimpleTag,允许我从dict-in-a-dict中访问值,并且视图产生了这个dict:

class VarListView(TemplateView):
    template_name = "lbv-list.html"

    def locations(self):
        return Location.objects.all()

    def locvars(self):

        dict = {}

        for v in LocationBasedVariable.objects.all():
            dict.setdefault(v.location.id, {})
            dict[v.location.id][v.definition.id] = v.value

        return dict

    def locvarnames(self):
        return LocationVariableDefinition.objects.all()

然后在模板中:

    <table class="table  table-striped table-condensed    striped">
        <thead>
        <tr>
            <th>&nbsp;</th>
            {% for loc in view.locations %}
                <th>{{ loc.name }}</th>
            {% endfor %}
        </tr>
        </thead>
        <tbody>
        {% for lv in view.locvarnames %}
            <tr>
                <th>{{ lv.name }}</th>
                {% for loc in view.locations %}
                    <td>{% get_dictitem2 view.locvars loc.id lv.id%}</td>
                {% endfor %}
            </tr>
        {% endfor %}
        </tbody>
    </table>

除了包含3个位置和5个变量之外,所有这些都有效,我得到 475 SQL查询,在我的笔记本电脑上运行大约需要3秒钟。

我可以做些什么来改善这个?我一直以为查询集会被缓存,但登录控制台的SQL似乎一遍又一遍地取出相同的集合。是否有提示我可以给Django结果不会改变,或只是一次只执行一次查询?

或者我应该只是将已排序的查询集传递给模板,并找出其中的行和列? (我通常这样做会涉及将状态保存在最后一个位置的模板中以与当前位置进行比较,我认为我不能在模板中这样做)

3 个答案:

答案 0 :(得分:2)

通常的方法是一次评估方法,并将结果添加到get_context_data中的模板上下文中。

class VarListView(TemplateView):
    def get_context_data(self, **kwargs):
        # Call the base implementation first to get a context
        context = super(VarListView, self).get_context_data(**kwargs)
        context['locations'] = self.locations()
        context['locvarnames'] = self.locavarnames()
        context['locvars'] = self.locvars()
        return context

然后,在您的模板中,移除view前缀,例如locations代替view.locations

答案 1 :(得分:1)

这里有几个选项,但我认为最好的方法是使用_id属性作为外键关系。 Django将外键的id存储在有问题的模型上,因此在您的情况下,您可以用以下代码替换您的循环:

for v in LocationBasedVariable.objects.all():
    dict.setdefault(v.location_id, {})
    dict[v.location_id][v.definition_id] = v.value

这意味着您不必查询相关表格以获取ID。

此外,您似乎正在为{% get_dictitem2 view.locvars loc.id lv.id%}中的每个项目调用view.locvarnames,而这些项目将转到数据库,加载所有LocationBasedVariable并填充该字典。

我可能想在视图中缓存它,所以你只填充一次:

class VarListView(TemplateView):
    template_name = "lbv-list.html"
    _location_based_variable_cache = None

    def locations(self):
        return Location.objects.all()

    def locvars(self):

        if not self._location_based_variable_cache:
            self._location_based_variable_cache = {}

            for v in LocationBasedVariable.objects.all():
                self._location_based_variable_cache.setdefault(v.location_id, {})
                self._location_based_variable_cache[v.location_id][v.definition_id] = v.value

        return self._location_based_variable_cache

    def locvarnames(self):
        return LocationVariableDefinition.objects.all()

答案 2 :(得分:1)

除了将.id更改为_id之外,您还应该查看实际需要返回的内容,因为您的for循环只会使用您只能使用values的ID过滤此内容以

开头
query_gives_locations.values('id')

您必须每次都获得.locvars,以便您可以使用with

将其移到外面
{% with locvars=view.locvars %}
{% for loc in view.locations %}
        <td>{% get_dictitem2 locvars loc_id lv_id%}</td>

{% endfor %}
{% endwith %}