减少 django 中的数据库查询

Posted

技术标签:

【中文标题】减少 django 中的数据库查询【英文标题】:Reducing db queries in django 【发布时间】:2011-09-24 17:18:16 【问题描述】:

我有一个视图,它可以搜索电影学分数据库,然后像这样转换和返回结果 --

# From the following results:
Avatar - James Cameron - director
Avatar - James Cameron - writer
Avatar - James Cameron - editor
Avatar - Julie Jones - writer
Crash - John Smith - director

# ...display in the template as:
Avatar - James Cameron (director, writer, editor)
Avatar - Julie Jones (writer)
Crash - John Smith (director)

但是,当我执行此转换并执行 print connection.queries 时,我访问数据库大约 100 次。这是我目前拥有的--

# in models
class VideoCredit(models.Model):
    video = models.ForeignKey(VideoInfo)

    # if the credit is a current user, FK to his profile,
    profile = models.ForeignKey('UserProfile', blank=True, null=True)
    # else, just add his name
    name = models.CharField(max_length=100, blank=True)
    # normalize name for easier searching / pulling of name
    normalized_name = models.CharField(max_length=100)

    position = models.ForeignKey(Position)
    timestamp = models.DateTimeField(auto_now_add=True)
    actor_role = models.CharField(max_length=50, blank=True)    

class VideoInfo(models.Model):
    title = models.CharField(max_length=256, blank=True)
    uploaded_by = models.ForeignKey('UserProfile')
    ...

类位置(models.Model): 位置 = models.CharField(max_length=100) ordering = models.IntegerField(max_length=3)

class UserProfile(models.Model):
    user = models.ForeignKey(User, unique=True)
        ...

在我看来,我正在以(name, video, [list_of_positions]) 的形式构建一个三元组列表,用于显示学分 --

    credit_set = VideoCredit.objects.filter(***depends on a previous function***)
    list_of_credit_tuples = []
    checklist = [] # I am creating a 'checklist' to see whether to append the positions
                   # list of create a new tuple entry
    for credit in credit_set:
        if credit.profile:  # check to see if the credit has an associated profile
            name = credit.profile    
        else:
            name = credit.normalized_name
        if (credit.normalized_name, credit.video) in checklist:
            list_of_keys = [(name, video) for name, video, positions in list_of_credit_tuples]
            index = list_of_keys.index((name, credit.video))
            list_of_credit_tuples[index][2].append(credit.position)
        else:
            list_of_credit_tuples.append((name, credit.video, [credit.position]))
            checklist.append((credit.normalized_name, credit.video))
    ...

最后,在我的模板中显示信用(注意:如果信用有个人资料,请提供指向用户个人资料的链接)--

% for name, video, positions in list_of_credit_tuples %
<p>% if name.full_name %
    <a href="% url profile_main user_id=name.id %">name.full_name</a>
    % else %
    name
    % endif %
    <a href="% url videoplayer video_id=video.id %">video</a>
    (% for position in positions %% ifchanged %position% endifchanged %% if not forloop.last %, % endif %% endfor %)
% endfor %

这个视图为什么以及在哪里创建了这么多数据库查询?我如何以及以何种方式使这个视图功能更有效/更好?谢谢你。

【问题讨论】:

我发现 list_of_keys = [(name, video) for name, video, locations in list_of_credit_tuples] 存在问题。你为什么要获得位置然后再使用它?您还试图迭代一个列表,除非您遗漏了某些内容,否则此时该列表为空 @John 我只检查(name, video) 而不是(name, video, position) 的原因是因为我想为该名称-视频对创建所有职位的列表。这是否回答了您所指的内容,还是有所不同? 正如我在下面所说,您的实际问题的答案与选择相关:list_of_credit_tuples = []。下一次 list_of_credit_tuples 被引用是在列表理解中。这意味着在你的 if 语句的那个​​分支中,它总是一个空列表。除非您以某种方式设置它,否则您会忽略 【参考方案1】:

尝试添加这个 Django sn-p,它返回查询的数量以及查询本身到您的模板:

http://djangosnippets.org/snippets/159/

这应该很容易告诉您泄漏的来源。

【讨论】:

【参考方案2】:

您需要查看 select_related() (https://docs.djangoproject.com/en/1.3/ref/models/querysets/#select-related) 以解决您的查询泄漏问题。如果您提前知道要查看与外键相关的模型数据,则需要添加 select_related。如果您知道只有几个外键就更好了,您可以只添加您需要的外键。

每当您看到 django 运行的查询数量超出您的预期时,select_related 几乎总是正确的答案

【讨论】:

除此之外,如果您知道您的数据库没有快速变化,那么 Caching is Your Friend™。我有一个网站,作者大约每 2 周发布一次,但是当他这样做时,由于关系而导致各种页面发生变化。我们只是缓存所有内容中的废话,一旦我们重新计算讨厌的页面,网站就会运行。 @John -- 我使用select_related() 添加了这个 -- credit_set = VideoCredit.objects.select_related(depth=1).filter(normalized_name__icontains=search),但它似乎并没有太大的不同。我是否将其添加到正确的位置? 尽管您应该可以使用,但请尝试 VideoCredit.objects.select_related('position').filter(normalized_name__icontains=search)。如果这似乎没有帮助,我将需要更多关于它正在运行的查询的确切信息,以便我可以寻找模式。 @John,结果差不多。 我看不到查询,所以也许我忽略了触发查询泄漏的其他内容。我建议使用 django-debug-toolbar 以便您可以很好地了解真正运行的查询以及运行了多少次。寻找模式(多次运行几乎相同的查询)

以上是关于减少 django 中的数据库查询的主要内容,如果未能解决你的问题,请参考以下文章

Django 视图中的大量查询

Python函数调用与数据库查询(django ORM)开销

Django 慢查询:将 django 过滤语句连接到数据库日志中的慢查询

通过 Nginx(或 apache)与 Django 进行实时(长轮询)连接——减少查询次数

减少查询中的结果数

Django中的数据库查询