Django :: 引用外键
我想创建一个网页,能够通过图片展示房产信息。
目前,我的房产模型大概是这样的(有些代码省略了):
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 个回答
要访问图片集合,你可以使用 room_list.picture_set.all()
这个方法。如果你想获取第一个图片,只需用 room_list.picture_set.first()
就可以了。在模板中,你可以这样写:{{ room_list.picture_set.first }}
。
简单回答你的问题:
{% 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]
虽然你可以这样做:
{% 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要好,而且比我最初的建议更容易实现(这并不自动意味着这是更好的解决方案)。