【问题标题】:Django: using values() and get_FOO_display()?Django:使用 values() 和 get_FOO_display()?
【发布时间】:2012-03-29 07:29:54
【问题描述】:

我正在尝试改进一些现有的代码,这些代码最初需要 3 分钟来准备一个大型数据表(然后由 Ajax 返回)。旧代码迭代一个大型查询集,从各种相关对象中收集信息。根据我的阅读和监控 SQL 日志,迭代查询集通常是一个坏主意,因为 SQL 是为每个项目执行的。相反,我一直在使用值在单个 SQL 语句中收集信息,然后对其进行迭代。使用这种技术,我将执行时间减少到 15 秒以下(我还没有完成)。但是因为我不再使用模型对象,所以我不能使用get_FOO_display()。有没有办法在使用 values() 时使用此功能?

简化,原文是:

for user in users:
   data.append(user.get_name_display())  # Appends 'Joe Smith'
return data

而新代码是:

for user in users.values('name'):
   data.append(user['name'])  # Appends 'JSmith001', which is incorrect
return data

另外,如果有其他方法可以保存模型对象的创建,但只需要在后端使用一条 SQL 语句,我很想知道。谢谢!

【问题讨论】:

  • 需要查看更多代码。这太笼统了,无法回答。
  • 对不起,我在上面添加了 cmets,但我不确定您还想看什么。

标签: python django django-queryset


【解决方案1】:

一般来说,使用返回模型对象的基于管理器的查询可能会更好、更容易。听起来您原始方法的问题不在于您正在迭代您的查询集(正如@ahmoo 所说,这不是性能问题),而是在您的迭代循环中您获得了额外的相关对象,需要一个或多个每条记录的附加查询。

有几种方法可以提高仍然返回模型实例的查询的性能:

  • 听起来最相关的是select_related(),它将有效地对初始查询进行表连接,以包含与外键相关的所有对象的数据。

  • 如果这还不够,您还可以使用 extra() 将数据添加到模型实例中,这样您就可以将子查询粘贴到 SQL 中。

  • 如果一切都失败了,您可以在 Manager 实例上使用 .raw() 方法执行 raw SQL queries,该方法仍将返回模型实例。

基本上,如果您可以在 SQL 中以每个实例为您提供一行的方式执行此操作,则可以在 Django 中执行此操作并获取模型实例。

不过,要回答您的原始问题,您可以通过 Field 类获取显示名称 - 这很丑:

def get_field_display(klass, field, value):
    f = klass._meta.get_field(field)
    return dict(f.flatchoices).get(value, value)

# usage
get_field_display(User, 'name', 'JSmith001')

【讨论】:

  • 嗯。当我执行User.objects.all() 然后迭代执行类似print user.name 的操作时,它只执行一个SQL 语句。但是,当我执行print user.team.name 时,它现在会为每个用户执行一条 SQL 语句,即使我是否使用 select_related 也是如此。
  • user.team 是外键关系还是多对多关系? select_related() 只跟随 ForeignKey 字段,我想。
  • 对不起,这是多对多的关系。是的,我读过它,并在意识到它只是目前的开发之前短暂地尝试了 prefetch_related。我认为这就是为什么我最终使用值将我的数据预填充到 dicts 中,除了我不能使用 get_FOO_display 之外,它工作得很好(最终修复时低至 2.7 秒)。
  • 查看我对上述get_FOO_display 问题的回答。
  • 谢谢!这样可行。我将研究使用基于管理器的查询的替代方法,用于我正在重构的接下来的 5 页之一。我在几个地方读到,加快速度的一种方法是使用 values() 并避免构建 django 的 ORM,但事情肯定会变得更加棘手。
【解决方案2】:

遍历查询集通常是个坏主意,因为 SQL 是为每个项目执行的

这不是真的。以下摘自the official docs

QuerySet 是可迭代的,它会在您第一次迭代时执行其数据库查询

我认为问题与代码中users 的定义有关。你给它分配了什么?

【讨论】:

  • 我们也做了很多外键跟随,这意味着我们点击了'user.team.name'。你是对的,他们只对简单对象执行一个查询,但是当我现在尝试调试它时,只要我遵循一个外键,日志中就会为每个用户都有一个单独的 SQL 语句。
  • @Nathan - 您是否尝试访问 user.team.nameuser.name?在您的原始帖子中,您似乎需要user.name,但后来您提到了user.team.name。如果您可以发布 Team 模型的定义并扩展您想要实现的目标,这可能是最好的。
  • 两者,以及来自不同关系的许多不同事物,它们是多对多和外键的组合。我认为现在有两个主要问题,1)是否可以在没有该模型对象实例的情况下获取模型的显示名称,以及 2)如何在没有多个 SQL 调用的情况下更好地迭代查询集。我在想#1 的答案是否定的,而#2 的答案要么是完全不同的 SO 帖子,要么是更多的代码自省。
猜你喜欢
  • 2014-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-11
  • 2014-07-14
  • 2015-10-17
  • 1970-01-01
相关资源
最近更新 更多