Django :: 引用外键

1 投票
3 回答
2691 浏览
提问于 2025-04-19 16:29

我想创建一个网页,能够通过图片展示房产信息。

目前,我的房产模型大概是这样的(有些代码省略了):

class Property(models.Model):
    PROPERTY_TYPE = (('Room', 'Room'), ('House', 'House'))
    type = models.CharField(max_length=100, choices=PROPERTY_TYPE)
    title = models.CharField(max_length=100, db_index=True)
    beds = models.IntegerField(null=True, blank=True)
    price = models.IntegerField()
    payment_term = models.CharField(choices=PAYMENT_TERM_CHOICES, max_length=50)

还有图片模型:

class Picture(models.Model):
    location = models.CharField(max_length=255)
    owner = models.ForeignKey(User)
    active = models.BooleanField(default=True)
    alt = models.CharField(max_length=100)
    parent_property = models.ForeignKey(Property, null=True)
    timestamp = models.DateTimeField(default=timezone.now)

在我的视图中,我是这样获取对象列表的:

room_list = Property.objects.all().filter(type='Room').order_by('timestamp')[6]

当我在传入的房间列表 room_list 中循环时,我想这样引用:

房间的图片列表中的第一张图片

我试着查阅了Django的官方文档,了解一对多的关系,但我似乎没能理解。

我该如何实现这个功能呢?

3 个回答

0

要访问图片集合,你可以使用 room_list.picture_set.all() 这个方法。如果你想获取第一个图片,只需用 room_list.picture_set.first() 就可以了。在模板中,你可以这样写:{{ room_list.picture_set.first }}

相关的Django文档

.first 方法的文档

0

简单回答你的问题:

{% for room in room_list %}
   <img src="{{ room.picture_set.first }}"/>
{% endfor %}

另外,还有一整章内容可以帮助你优化这个问题,具体可以查看这里

顺便说一下:

这个查询只会给你第七个房子

room_list = Property.objects.all().filter(type='Room').order_by('timestamp')[6]

你可能想要的是[:6]

而且在.filter()之前,你不需要调用.all()

room_list = Property.objects.filter(type='Room').order_by('timestamp')[:6]
1

虽然你可以这样做:

{% for room in room_list %}
   <img src="{{ room.picture_set.first }}"/>
{% endfor %}

但我不推荐这样。原因是,对于每一个房间列表中的项目,你都要去数据库查询一次图片。所以如果你有30个项目的话,你就会向数据库发起31个请求。

最简单的办法是,在你的房产模型中添加一个新的字段,比如叫default_picture,这个字段会指向一张图片,默认值为None。保存图片后,你就把那张图片的ID填进去(你用的是哪张图片,以及如何处理没有图片的记录,这取决于你的业务逻辑)。然后你可以这样获取记录:

room_list = Property.objects.filter(
    type='Room'
).select_related(
    'default_picture'
).order_by('timestamp')[6]

这样就只会发起一次查询。虽然还有其他方法可以达到同样的效果,但那些方法会更复杂。

小贴士:你不需要按时间戳字段排序(除非你修改了它的值),你可以用ID来排序,因为ID是按顺序生成的。这种操作会更便宜(至少在Postgres中是这样)。

更新:正如@JamesLin建议的,你可以使用这样的方式:

room_list = Property.objects.filter(
    type='Room'
).prefetch_related(
    'picture_set'
)

这会被转换成两个查询,一个是获取房产列表,另一个是获取所有与这些房产相关的图片记录(Django会自动把它们连接起来)。这对程序员来说比较懒惰,但要考虑到你是为每个房产加载所有的图片记录。不过,这肯定比使用picture_set要好,而且比我最初的建议更容易实现(这并不自动意味着这是更好的解决方案)。

撰写回答