【问题标题】:Django model polymorphism without Multi-Table Inheritance and additional JOIN没有多表继承和额外 JOIN 的 Django 模型多态性
【发布时间】:2014-02-21 12:45:48
【问题描述】:

我对 Django 很陌生,我正在尝试在 Django 模型中实现多态性,但我不知道该怎么做。在继续之前,我必须说我已经尝试过 django-model-utils 和 django-polymorphism,但它们并没有完全符合我的要求。

我有一个名为 Player 的模型,每个玩家都有一个角色,每个角色都有不同的行为(即他们的方法返回不同的值):

class Player(models.Model):
    username=models.TextField()
    role=models.ForeignKey(Role)    #Role is another model with a field called ’name'

    def allow_action(self)
         #some stuff

class RoleA():
    def allow_action(self):
         #some specific stuff

class RoleB():
    pass

我希望每次检索 Player 的任何实例(例如通过 Player.objects.filter(...))时,每个实例的 allow_action() 方法都会被特定类(RoleA、RoleB、 etc...) 或者如果相关子类没有同名调用的方法(RoleA、RoleB 等...是存储在 Player.role.name 中的相同角色名称),则使用 Player 中提供的默认方法。

约束:

  1. 由于子类(RolaA、RoleB 等)不添加新字段而只覆盖方法,所有数据都必须存储在 Player 的表中,所以我不想使用 Django 多表继承更类似于代理。

  2. 我不想执行额外的 JOIN 来确定特定的子类类型,因为所需的所有信息都存储在 Player 的表中。

我认为这是一种标准的多态模式,但我不知道如何在 Django 中为所有玩家使用同一张表来实现它(我已经实现了这种多态,但没有链接到 Django 模型)。我已经看到 Django 有一种称为“代理”的继承,但它不允许像 Player.objects.filter(...) 那样进行查询并获取方法被自定义方法覆盖的实例(或者至少这是我所理解的)。

提前致谢。

【问题讨论】:

  • 为什么django-polymorphic 不适合您的需求?除非我遗漏了什么,否则您可以完全按照自己的意愿行事。
  • @PeteTinkler 根据这个example 它违反了我的第一个约束,即使用多表继承将为每个子类创建一个新表,但我不想要这些表,因为我的数据库将是被未使用的表污染(我将有很多 RoleA、RoleB、RoleC 等)。我认为这同样适用于你下面的 sn-p。
  • 那些数据库表不会被使用。您将创建 RoleA...RoleX 实例并使用 Role 基类中的 name 属性。
  • 我不知道 Django 是否会使用这些表,但它们在逻辑上不是必需的。我按照article 解决了这个问题,它没有使用最佳编程实践(它涉及 self.__class__ 赋值),但它用几行代码解决了我的问题。

标签: python django inheritance django-models


【解决方案1】:

免责声明:我没有使用过 django-polymorphic,此代码基于 5 分钟扫描文档,完全未经测试,但我有兴趣看看它是否有效:

from polymorphic import PolymorphicModel


class Role(PolymorphicModel):
    name = models.CharField()


class RoleA(Role):
    def allow_action(self):
        # Some specific stuff...


class RoleB(Role):
    pass


class Player(models.Model):
    username=models.TextField()
    role=models.ForeignKey(Role)    #Role is another model with a field called ’name'

    def allow_action(self)
        if callable(getattr(self.role, "allow_action", None):
            self.role.allow_action()
        else:
            # default action...

现在我相信您应该能够创建 Role、RoleA 或 RoleB 的实例,并让 Player 在外键中指向它。在 Player 实例上调用 allow_action() 将检查 Role(或 RoleARoleB 等)的实例是否具有可调用属性 allow_action(),如果是,它将使用它,否则它将使用默认值。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-04
    • 2011-03-11
    • 2014-06-01
    • 2023-03-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多