问题摘要

我有一个基于类的列表视图,它只显示每个对象的链接以及来自一些对象字段的一些数据.链接的href是使用我在模型本身中编写的get_absolute_url方法生成的.问题是每次运行Get_Abte_url时都会查询数据库.这会导致许多重复查询(将来对象更多时,这将是一个问题).

try 的解决方案

我的Get_Abte_url方法从我的模型访问一些ForeignKey字段,所以我try 在我的视图中为我的查询集使用.Select_Related().然而,这并没有改变任何事情.

问题

如何通过运行Get_Abte_url来消除重复的查询?

Code

Models.py

class LanguageLocale(models.Model):
    """model for representing language locale combinations"""

    class LANG_CODES(models.TextChoices):
        EN = 'en', _('English')
        ES = 'es', _('español')
        QC = 'qc', _("K'iche'")

    lang = models.CharField(max_length=2, choices=LANG_CODES.choices, blank=False)

class Scenario(models.Model):
    """model for representing interpreting practice scenarios"""

    scenario_id = models.CharField(max_length=20, primary_key=True)

    lang_power = models.ForeignKey(LanguageLocale)
    lang_primary_non_power = models.ForeignKey(LanguageLocale)

    class SCENARIO_STATUSES(models.TextChoices):
        PROD = 'PROD', _('Production')
        STGE = 'STGE', _('Staged')
        EXPR = 'EXPR', _('Experimental')

    status = models.CharField(
        max_length=4, choices=SCENARIO_STATUSES.choices, default='EXPR')

    def get_absolute_url(self):
        """Returns the URL to access a detail record for this scenario."""
        return reverse('dialogue-detail', kwargs={
            'lang_power': self.lang_power.lang,
            'lang_primary_non_power': self.lang_primary_non_power.lang,
            'pk': self.scenario_id
            }
        )

Views.py

class ScenarioListView(generic.ListView):
    """View class for list of Scenarios"""

    queryset = Scenario.objects.select_related(
        'domain_subdomain', 'lang_power', 'lang_primary_non_power'
    )
    
    demo = Scenario.objects.get(scenario_id='demo')
    prod = Scenario.objects.filter(status='PROD')
    staged = Scenario.objects.filter(status='STGE')
    experimental = Scenario.objects.filter(status='EXPR')
    
    extra_context = {
        'demo': demo,
        'prod': prod,
        'staged': staged,
        'experimental': experimental,
    }

Scenario_list.html

{% extends "home.html" %}

{% block content %}
  <h1>Practice Dialogues</h1>
  <section>
    {% if prod %}
      <ul>
        {% for scenario in prod %}
          <li>
            <a href="{{ scenario.get_absolute_url }}">{{  scenario.title }}</a> ({{ 
            scenario.domain_subdomain.domain }})
          </li>
        {% endfor %}
      </ul>
    {% else %}
      <p>There are no practice scenarios. Something went wrong.</p>
    {% endif %}
  </section>
  {% if user.is_staff %}
    <section>
      {% if staged %}
        <article>
          <h2>Staged Dialogues</h2>
          <ul>
            {% for scenario in staged %}
              <li>
                <a href="{{ scenario.get_absolute_url }}">{{  scenario.title }}</a> ({{ 
                scenario.domain_subdomain.domain }})
              </li>
            {% endfor %}
          </ul>
        </article>
      {% endif %}
      {% if experimental %}
        <article>
          <h2>Experimental Dialogues</h2>
          <ul>
            {% for scenario in experimental %}
              <li>
                <a href="{{ scenario.get_absolute_url }}">{{  scenario.title }}</a> ({{ 
                scenario.domain_subdomain.domain }})
              </li>
            {% endfor %}
          </ul>
        </article>
      {% endif %}
    </section>
  {% endif %}
{% endblock %}

重复查询读数(从Django调试工具栏)

此查询重复3次:

SELECT "scenario_languagelocale"."id",
       "scenario_languagelocale"."lang_locale",
       "scenario_languagelocale"."lang"
  FROM "scenario_languagelocale"
 WHERE "scenario_languagelocale"."id" = 1
 LIMIT 21 6 similar queries.  Duplicated 3 times.

此查询重复两次:

SELECT "scenario_languagelocale"."id",
       "scenario_languagelocale"."lang_locale",
       "scenario_languagelocale"."lang"
  FROM "scenario_languagelocale"
 WHERE "scenario_languagelocale"."id" = 3
 LIMIT 21 6 similar queries.  Duplicated 2 times.

推荐答案

我建议notextra_context.这将防止在两个请求之间重新计算查询,这意味着如果您因此添加了额外的Scenario,并且您没有重新启动服务器,那么当您再次请求场景的时候,它将不会"更新"列表.demo查询更有问题,因为它会在您对服务器进行start时立即运行,因此,如果您在具有数据库的服务器上运行此查询,而没有这样的演示Scenario,则可能会引发错误.

但是,您可以使用queryset,它已经在这里对必要的关系执行了.select_related(…),因此:

from django.shortcuts import get_object_or_404


class ScenarioListView(generic.ListView):
    """View class for list of Scenarios"""
    queryset = Scenario.objects.select_related(
        'domain_subdomain', 'lang_power', 'lang_primary_non_power'
    )

    def get_context_data(self, *args, **kwargs):
        return super().get_context_data(
            *args,
            **kwargs,
            demo=get_object_or_404(self.queryset, scenario_id='demo'),
            prod=self.queryset.filter(status='PROD'),
            staged=self.queryset.filter(status='STGE'),
            experimental=self.queryset.filter(status='EXPR'),
        )

Django相关问答推荐

在Django中使用Generil.ListView类时,分页不起作用

社工/社工简戈

有没有办法在Django中按需/点击仅获取和序列化一部分数据以提高性能?

在 settings.py 中指定 Django 测试数据库名称

django-rest-framework 如何使模型序列化器字段成为必需

多租户 Django 应用程序:根据请求更改数据库连接?

django 用一个提交按钮提交两种不同的表单

Django 模型将外键设置为另一个模型的字段

PyMongo vs MongoEngine for Django

可以在 Django 抽象模型中使用多重继承吗?

django:manytomanyfield 和 through 如何出现在 admin 中?

我应该如何在我的模型中使用 DurationField?

如何使 Django QuerySet 批量删除()更高效

如何将 django csrf 令牌直接嵌入 HTML?

ProgrammingError: 安装 Psycopg2 后,关系django_session不存在错误

RemovedInDjango18Warning:不推荐创建没有fields属性或 exclude属性的 ModelForm

Django:DoesNotExist从何而来?

Django - 安装 mysqlclient 错误:需要 mysqlclient 1.3.13 或更高版本;你有 0.9.3

如何从 django 请求中获取完整的 url

防止 django 管理员转义 html