我对自定义表单的模型看起来像这样。
class Form(models.Model):
# some fields
class FormSection(models.Model):
form = models.ForeignKey(Form, related_name='section_set')
class FormWidget(models.Model):
section_set = models.ManyToManyField(FormSection, related_name='widget_set')
class FormEntry(models.Model):
user = models.ForeignKey(User, related_name="form_entry_set")
form = models.ForeignKey(Form)
date = models.DateTimeField(default=datetime.datetime.now)
class SectionEntry(models.Model):
section = models.ForeignKey(FormSection)
form_entry = models.ForeignKey(FormEntry, related_name="section_entry_set")
class WidgetEntry(models.Model):
widget = models.ForeignKey(FormWidget)
section_entry = models.ForeignKey(SectionEntry, related_name="widget_entry_set")
value = models.CharField(max_length=255)
对于我的一个观点,我需要检索:
根据用户列表,在特定时间段内为列表中的每个用户获取所有FormEntry
个。并且对于每个FormEntry
,获取表单数据(WidgetEntry.value)
并在字典中将其结构化。
{"<form_entry_pk>": {
"date": "2015-06-26",
"<section_name>": {
"<widget_name>": "<widget_value>"
},
"<section_name>": {
"<widget_name>": "<widget_value>"
},
"<section_name>": {
"<widget_name>": "<widget_entry_value>",
"<widget_name>": "<widget_entry_value>",
"<widget_name>": "<widget_entry_value>",
"<widget_name>": "<widget_entry_value>",
"<widget_name>": "<widget_entry_value>",
"<widget_name>": "<widget_entry_value>",
"<widget_name>": "<widget_entry_value>",
"<widget_name>": "<widget_entry_value>",
"<widget_name>": "<widget_entry_value>"
}
},
"<form_entry_pk>": {
...
},
...
}
目前,我正在通过循环遍历每个查询集中的项来检索数据。像这样的东西。
for user in users:
form_data = {}
form_entries = user.form_entry_set.filter(form=form, date__range=[start_date, end_date]).order_by('date')
for form_entry in form_entries:
form_data[form_entry.pk] = {}
form_data[form_entry.pk]['date'] = form_entry.date
for section_entry in form_entry.section_entry_set.all():
form_data[form_entry.pk][section_entry.section.name] = {}
for widget_entry in section_entry.widget_entry_set.all():
form_data[form_entry.pk][section_entry.name][widget_entry.widget.name] = widget_entry.value
这会产生我想要的结果。但是花了很长时间。在某些情况下最多2分钟。在使用django-debug-toolbar进行一些调试之后,我注意到有大量重复的SQL查询。 (即4031 queries including 4024 duplicates
)
我的问题是:我该怎么做才能减少查询次数。
我尝试使用defer()
和only()
(在代码中排除使其更具可读性)。但他们似乎并没有那么多帮助。
提前感谢!
答案 0 :(得分:2)
我认为这里的关键是在初始查询中使用select_related
。假设你的模型是正确的外键,那应该解决顶部的所有关系(通过跟随模型中定义的FK在幕后生成JOIN
查询)。
所以第一个查询集就变成了这样:
form_entries = user.form_entry_set.filter(form=form, date__range=[start_date, end_date]).order_by('date').select_related()
然后,您可以从返回的查询集访问各种模型中的所有列,这将消除嵌套循环的需要。 (您应该能够遍历查询集本身。)
根据OP的评论进行编辑:
prefetch_related
处理除 FK 和一对一之外的其他类型的关系,结果select_related
仅限于此。由于您的模型中定义了ManyToManyField
,因此prefetch_related
可能会在您的具体情况下更好地运作。