从 Django 模板中的特定相关对象查询数据

Posted

技术标签:

【中文标题】从 Django 模板中的特定相关对象查询数据【英文标题】:Querying data from a specific related object in Django template 【发布时间】:2011-06-22 03:07:43 【问题描述】:

我正在尝试使用 Django 显示来自多个模型的信息表,但我不确定这样做的最佳实践是什么。这是我的 models.py 的精简版:

from django.contrib.auth.models import User

class Widget(models.Model):
    name = models.CharField(max_length=50)
    pass

class Finished_Widget(models.Model):
    user = models.ForeignKey(User)
    thing = models.ForeignKey(Thing)
    created = models.DateTimeField(editable=False)

这是一个非常简单的设置,用户和小部件通过未知数量的 Finished_Widget 对象连接。我想在我的 html 中显示小部件名称的实例列表和最近完成的小部件的日期。显然下面的代码不起作用,但希望它能说明我希望输出的数据。

% for widget in widget_list %
<p> widget.name </p>
<p> widget__Finished_Widget.latest.created </p>
% endfor %

我想了很多不同的方法来解决这个问题,包括:

通过视图将所需日期作为附加属性附加 在视图中创建字典并在模板中调用它 ManyToManyField 使用 'through' 参数 向 Widget 添加 meta 调用最新 Finished_Widget 的创建日期

很遗憾,我无法完成这些工作,我不确定哪些(如果有的话)是正确的。

注意:我已经在 SO 上发现了很多关于这个问题的变体,这使得这非常接近于欺骗,但它们似乎对我没有多大帮助,因为需要将 Finished_Widget 列表缩小到那些匹配用户 ID,匹配 Widget ID,并且只有最新的。

【问题讨论】:

【参考方案1】:

根据传入user 对象的需要更新了答案。

在您看来,最简单的方法是处理这个问题,但它有它的问题。

for widget in widget_list:
    widget.latest_finished_widget = widget.finished_widget_set.filter(user=request.user).latest('created')

% for widget in widget_list %
    <p> widget.name </p>
    <p> widget.latest_finished_widget </p>
% endfor %

虽然这段代码很容易编写,但每次迭代都要执行一次数据库查询。

一种快速的解决方案称为查询分区。使用 2 个查询和 python 尽可能高效地填充反向关系,而无需使用 SQL。

widgets = Widget.objects.filter(...)

finished_widget_map = dict( 
    [(x['thing__id'], x['created']) for x in 
        Finished_Widget.objects.filter(thing__in=widgets, user=request.user).values_list('thing__id', 'created').order_by('created')])
    # this ensures we only select what we need, thing__id and the created column
    # ordering by created will ensure only the last (latest) will appear in our dictionary

for widget in widgets:
    widget.latest_finished_widget_created = finished_widget_map.get(widget.id, 'No Widget')


% for widget in widgets %
     widget.name 
     widget.latest_finished_widget_created 
% endfor %

不过,总会有取舍。如果有很多 Finished_Widgets python 无意义地试图填充字典,这可能会很慢。

【讨论】:

Yuji,我想你可能不小心强调了为什么我在这方面遇到了这么多麻烦。你能解释一下你从哪里得到'finished_widget_set'吗?看起来它可能是我找不到文档的 django 模板语法的某些方面?还是你假设我会在别处定义它? @IanWhalen,好问题!是的,django 自动生成方便的方法来“向后”检索关系docs.djangoproject.com/en/dev/topics/db/queries/…——默认情况下:fieldname_set。尽管如此,它仍然是低效的,因为您可能会想象每个小部件 1 个查询。当您有一个对象并想要所有 100 个相关对象时,它会更有用,而不是 100 个对象,每个对象都有 1 个您想要的相关对象。 PS:模板点表示法是严格基于python的,除了点表示法之外没有特殊的模板语法:)docs.djangoproject.com/en/dev/ref/templates/api/#render它执行python dict查找、attr查找、索引查找,然后默默地失败.所以如果有一个你不认识的点符号方法,它是一个 python dict key、attr 或 index! 嗨,Yuji,我不想“不回答”这个问题,但我意识到你给出的第一个建议存在相当大的问题。 'widget.finished_widget_set.latest' 部分将返回整个数据库中最新的finished_widget,而我需要它只显示当前经过身份验证的用户的最新版本。但是,我被阻止了,因为您无法在模板中传递参数,因此“widget.finished_widget_set(user=user).latest”将不起作用。这有意义吗? @IanWhalen:那么您将不得不通过您的视图在您的对象上设置一个属性(模型方法不应处理请求)。我强烈建议我指出的第二种方法,尽管如此!更新示例【参考方案2】:

好吧,我认为这里没有足够的信息来完全回答您的问题,但是解决仅返回最新 Finished_Widget 问题的一个简单方法是向 User 模型添加一个名为“latest_widget”的方法:

class User(models.Model):
    #...
    def latest_widget(self):
        return self.widgets.all().order_by('-created')[0]

从技术上讲,您可以将其添加到 Finished_Widget 模型中,尽管它没有多大意义。如果您使用 Django 的 auth 模块中的 User 模型,您可能希望对模型进行子类化以添加此功能。

一旦你有了这个方法,从模板中调用它就很简单了。如果您有一个用户列表,例如名为 user_list,您可以像这样显示每个用户和他/她的最新小部件:

% for uu in user_list %
<p>
    uu.username: uu.latest_widget.name
</p>
% endfor %

【讨论】:

对不起,如果我不够清楚,沃尔特,但我将遍历小部件列表,而不是用户,我将寻求从最新访问“创建”字段Finished_Widget 对象,不是最新的 Widget 对象。 他似乎正在迭代widget_list,这可能是Widget 实例——所以它必须进入那里。无论如何,您的想法都有效:) 编辑:浏览器刷新失败 啊,是的,我现在明白了。好吧,对我来说,制定解决方案最有帮助的是查看一些示例数据(因为它存在于您的模型和数据库中)以及它应该如何显示在页面上。

以上是关于从 Django 模板中的特定相关对象查询数据的主要内容,如果未能解决你的问题,请参考以下文章

如何获取与传递给 Django 模板的查询集相关的查询集对象

Django 1.6:在另一个模板中显示特定模型对象

用于从相关查询中列出所有 ForeignKey 对象的 Django 查询

如何使用附加过滤的相关对象作为 Django 中的字段来获取结果?

django相关查询过滤器,搜索有无相关项目的项目

Django:获取相关对象的相关对象并传递给模板