【问题标题】:Django sharing anotations between related ItemsDjango 在相关项目之间共享注释
【发布时间】: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 我的建筑→楼层→房间⋄预订示例类似于我的实际模型,这些模型更抽象,在我解决问题之前肯定需要一些时间来解释。我真正处理的中间数据偶尔会发生变化。非规范化仍然是一种选择,但我正在寻找一种方法来分享其注释方面,因为即使在这个问题之外,也有一些地方我已经多次编写了类似的复杂注释。

标签: django django-annotate


【解决方案1】:

在写这篇文章时,我有一个想法,我可以编写一个方法,允许为与房间的关系传递前缀。这样我就可以从任何与房间相关的东西中调用它,它会得到正确的东西:

class RoomManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().annotate(
            roomname=RoomManager.roomname()
        )

    @staticmethod
    def roomname(prefix=''):
        return Concat(
            prefix + 'name',
            V(', #'),
            prefix + 'number'
            V(' Floor '),
            prefix + 'floor__number'
            V(', '),
            prefix + 'floor__building__name',
            output_field=models.CharField()
        )

在 BookingManager 中,我可以在 RoomManager.roomname('room__') 上进行注释

它更干净,我会在其他地方使用它,但感觉不是很聪明。

【讨论】:

    【解决方案2】:

    这样的事情怎么样?

    class Room(models.Model):
        number = models.PositiveIntegerField()
        name = models.CharField(..)
        floor = models.ForeignKey('Floor')
    
        def __str__(self):
            return '%s, %s %s' % (
                self.name,
                self.number,
                self.floor
            )
    
    class Floor(models.Model):
        number = models.PositiveIntegerField()
        building = models.ForeignKey('Building')
    
        def __str__(self):
            return 'Floor %d, %s' % (
                self.number,
                self.building
            )
    
    class Building(models.Model):
        name = models.CharField(...)
    
        def __str__(self):
            return '%s' % (
                self.name
            )
    

    【讨论】:

    • 我想我只是在问题的 cmets 中解决了这个问题,但具体来说,我只想要完整的空间地址,我只想要它在两个地方(来自空间查询集和预订查询集)所以我可以在不进行大量连接或数十万(严重)递归查询的情况下列出它们。注释是我确定正确使用的方式,问题是在模型之间共享此注释。
    • 啊,是的,我现在明白了。好吧,您的经理是重用它的好方法,但就性能而言,@serg 有一个好处 - 缓存数据比让数据库完成工作要好得多(需要一些缓存失效,但我想编辑并不经常)-磁盘空间比处理能力便宜。
    猜你喜欢
    • 2023-04-10
    • 1970-01-01
    • 1970-01-01
    • 2017-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-27
    • 1970-01-01
    相关资源
    最近更新 更多