Django在相关项目之间共享anotations

时间:2016-09-24 20:55:34

标签: django django-annotate

考虑一个房间预订系统。您可能拥有建筑物,楼层,房间模型以及预订。我们根据房屋的建筑和楼层给房间命名:

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之前做了一个可怕的多查询字符串构建器。

但现在最重要的是,我有预订。每个预订实例都链接到一个房间。在许多情况下,列出预订,我实际上也列出了房间名称。

我所做的就是写一个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的,在这里我复制并粘贴一个巨大的杂乱注释。这太恶心了。

我的问题是......还有其他办法吗?

2 个答案:

答案 0 :(得分: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__')

上进行注释

它比较干净,我会在别处使用它,但它感觉不是很聪明。

答案 1 :(得分:0)

这样的事情怎么样?

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
        )
相关问题