繁体   English   中英

Django嵌套查询性能

[英]Django Nested Query Performance

我对自定义表单的模型看起来像这样。

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() (在代码中排除使其更具可读性)。 但他们似乎并没有那么多帮助。

预先感谢!

我认为这里的关键是在初始查询中使用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可能会在您的特定情况下更好地工作。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM