增强SQLAlchemy多态标识的语法

4 投票
2 回答
2000 浏览
提问于 2025-04-16 08:37

我有一个声明式的基类 Entity,它把列 name 定义为多态的,也就是说它可以有不同的表现形式,比如:

class Entity(DeclarativeBase):
    name = Column('name', String(40))
    __mapper_args__ = {'polymorphic_on':name}

在子类中,我现在可以这样写:

class Experiment(Entity):
    __mapper_args__ = {'polymorphic_identity': "experiment"}

这样就可以了。不过,我想让我的库的用户在创建子类时更方便,所以我希望能做到以下几点:

  • 提供一种更简单的语法,不要像默认的那样复杂。比如在类里面简单地赋值 poly_id = "exp",或者使用类装饰器。
  • 如果没有给出多态身份,就从子类的名字中提取名称。

我尝试用元类来实现这个(只实现第二部分):

from sqlalchemy.ext.declarative import DeclarativeMeta

class Meta(type):
    def __init__(cls, classname, bases, dict_):
        dict_["__mapper_args__"] = {'polymorphic_identity': classname}
        return super(Meta, cls).__init__(classname, bases, dict_)

class CombinedMeta(Meta, DeclarativeMeta):
    pass

class Experiment(Entity):
    __metaclass__ = CombinedMeta

我认为我的 Meta 应该在调用 DeclarativeMeta 之前设置名称,但似乎没有成功。所以要么是 DeclarativeMeta 没有看到这个变化,因为我搞错了方法解析顺序(MRO),要么我做的事情本身就是错的。我需要改变什么,或者在 SQLAlchemy 中已经有类似的东西吗?

2 个回答

1

最近的sqlalchemy文档展示了如何使用混合类来实现这些功能(我不确定这是否只在版本>= 0.6中有效,还是现在只是文档写得更清楚了)

http://docs.sqlalchemy.org/en/rel_0_9/orm/extensions/declarative/mixins.html

7

真是丢人,不过这个问题其实很简单,只要不使用 dict_,而是直接在 cls 上设置属性就可以解决了。

class Meta(DeclarativeMeta):
    def __init__(cls, *args, **kw):
        if getattr(cls, '_decl_class_registry', None) is None:
            return # they use this in the docs, so maybe its not a bad idea
        cls.__mapper_args__ = {'polymorphic_identity': cls.__name__}
        return super(Meta, cls).__init__(*args, **kw)

class Experiment(Entity):
    __metaclass__ = Meta

撰写回答