【发布时间】:2016-09-24 20:55:34
【问题描述】:
考虑一个房间预订系统。您可能有建筑物、楼层、房间模型以及预订。我们根据房间的建筑和楼层给房间命名:
class Room(models.Model):
number = models.PositiveIntegerField()
name = models.CharField(..)
floor = models.ForeignKey('Floor')
def __str__(self):
return '%s, #%d Floor %d, %s' % (
self.name,
self.number,
self.floor.number,
self.floor.building.name
)
当您处理数百个(例如管理列表、大型报告等)时,效率非常低,因此我开始编写这样的管理器:
class RoomManager(models.Manager):
def get_queryset(self):
return super().get_queryset().annotate(
roomname=Concat(
'name',
V(', #'),
'number'
V(' Floor '),
'floor__number'
V(', '),
'floor__building__name',
output_field=models.CharField()
),
)
这行得通。它做了我想做的一切。它速度很快,而且我已经对 __str__ 进行了重新设计,以在它执行可怕的多查询字符串构建器之前执行 if hasattr(self, 'roomname'): return self.roomname。
但现在最重要的是,我有预订。每个 Booking 实例都链接到一个房间。列出Bookings的地方很多,其实我也列出了房间名称。
我所做的是编写一个 BookingManager:
class RoomManager(models.Manager):
def get_queryset(self):
return super().get_queryset().annotate(
roomname=Concat(
'room__name',
V(', #'),
'room__number'
V(' Floor '),
'room__floor__number'
V(', '),
'room__floor__building__name',
output_field=models.CharField()
),
)
但到底是什么?我在重复自己。 Django 是关于 DRY 的,在这里我复制并粘贴了一个巨大的混乱注释。真恶心。
我的问题是……还有其他方法吗?
【问题讨论】:
-
这看起来像是您试图将视图强制到模型上 - 如果您需要一个返回实例的完整“面包屑”的方法,您可以只编写一个方法(在每个模型上)将其名称附加到其父级的面包屑上 - 这将递归到基类 - 为什么使用查询集注释?
-
如果您确实需要编辑查询集,我认为您也可以使用此方法构建注释..
-
如果房间名称随处可见,为什么不将其添加为 db 列?楼层/建筑物/房间号组合听起来不会经常改变。
-
@MarZab 正如我在问题中所说,我列出了很多地方。对每一层的一个字段进行超深度连接并不明智。而且它不是面包屑,它始终只是房间的完整“地址”。
-
@serg 我的建筑→楼层→房间⋄预订示例类似于我的实际模型,这些模型更抽象,在我解决问题之前肯定需要一些时间来解释。我真正处理的中间数据偶尔会发生变化。非规范化仍然是一种选择,但我正在寻找一种方法来分享其注释方面,因为即使在这个问题之外,也有一些地方我已经多次编写了类似的复杂注释。