在Django模板中查询特定相关对象的数据
我正在尝试使用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)
这个设置很简单,User和Widget通过一些未知数量的Finished_Widget对象连接在一起。我想在我的HTML中显示的是Widget名称的实例列表,以及最近完成的Widget的日期。显然,下面的代码是不能工作的,但希望它能说明我想要输出的数据。
{% for widget in widget_list %}
<p>{{ widget.name }}</p>
<p>{{ widget__Finished_Widget.latest.created }}</p>
{% endfor %}
我考虑过几种不同的解决方案,包括:
- 通过视图将所需的日期作为额外属性添加
- 在视图中创建一个字典,然后在模板中调用它
- 使用'通过'参数的ManyToManyField
- 在Widget中添加元数据,以调用最新Finished_Widget的创建日期
不幸的是,我还没有成功实现这些方法,也不确定哪种方法是正确的,或者是否有任何一种方法是对的。
注意:我在StackOverflow上找到了很多关于这个问题的变体,这让这个问题几乎是重复的,但它们似乎对我没有太大帮助,因为我需要将Finished_Widget列表缩小到匹配用户ID、匹配Widget ID,并且仅是最新的一个。
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 的认证模块里的 User 模型,可能需要对这个模型进行扩展,才能添加这个功能。
一旦你有了这个方法,从模板里调用它就很简单了。比如,如果你有一个用户列表,叫做 user_list,你可以这样显示每个用户和他/她最近的 Widget:
{% for uu in user_list %}
<p>
{{uu.username}}: {{uu.latest_widget.name}}
</p>
{% endfor %}
根据需要传入 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 %}
虽然这段代码写起来很简单,但它在每次循环时都会执行一次数据库查询,这样效率不高。
一个快速的解决方案叫做查询分区。可以使用两个查询和 Python 来尽可能高效地填充反向关系,而不需要去数据库。
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 可能会无意义地尝试填充一个字典,这样会变得很慢。