【问题标题】:Querying data from a specific related object in Django template从 Django 模板中的特定相关对象查询数据
【发布时间】: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,并且只有最新的。

【问题讨论】:

    标签: python django django-models


    【解决方案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 实例——所以它必须进入那里。无论如何,您的想法都有效:) 编辑:浏览器刷新失败
    • 啊,是的,我现在明白了。好吧,对我来说,制定解决方案最有帮助的是查看一些示例数据(因为它存在于您的模型和数据库中)以及它应该如何显示在页面上。
    猜你喜欢
    • 2013-01-24
    • 2020-07-16
    • 1970-01-01
    • 2013-05-06
    • 2018-09-13
    • 2021-02-25
    • 2018-10-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多