问题汇总
我有一个基于类的列表视图,它只显示每个对象的链接沿着来自一些对象字段的一些数据。链接的href
是使用我在模型本身中编写的get_absolute_url
方法生成的。问题是每次运行get_absolute_url时都会查询数据库。这会导致许多重复查询(将来,如果有更多的对象,这将是一个问题)。
尝试解决方案
我的get_absolute_url方法从我的模型中访问一些ForeignKey字段,所以我尝试在视图中为我的查询集使用.select_related()。但这并没有改变什么。
提问
如何从运行get_absolute_url中消除重复查询?
代码
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.
重复查询2次:
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.
1条答案
按热度按时间46scxncf1#
我建议 * 不要 * 使用
extra_context
。这将防止在两个请求之间重新评估查询,这意味着如果您因此添加了额外的Scenario
,并且您没有重新启动服务器,那么当您再次请求场景时,它将不会“更新”列表。demo
查询的问题更大,因为它会在您启动服务器时立即运行,因此如果您在带有数据库的服务器上运行它,而没有这样的demoScenario
,可能会引发错误。但是,您可以使用
queryset
,它已经在必要的关系上执行了.select_related(…)
,因此: