SQLAlchemy:如何将常见映射逻辑添加到自定义基类(使用声明式映射)?

2 投票
1 回答
570 浏览
提问于 2025-04-18 13:47

我有一个项目,里面的每个表都有一些共同的字段,比如状态(status),我想给这些字段起个别名。有没有办法不需要手动在每个类里添加别名呢?比如,现在我的代码是这样的:

from core import foo_table, bar_table, Status 

Base = declarative_base()

def CustomBase(object):
   @property
   def status(self):
      return Status(self._status)
  ...

def Foo(Base, CustomBase):
   __table__ = foo_table
   _status = foo_table.c.status
   ...


def Bar(Base, CustomBase):
   __table__ = bar_table
   _status = bar_table.c.status
   ...

理想情况下,我希望能在 CustomBase 这个基础类里设置我的 _status 别名,而不是在 Foo 和 Bar 里设置,或者我希望我的项目能在每次加载扩展 CustomBase 的类时自动添加这个别名。这样做可行吗?还是说我在尝试的方向上有问题?我知道如果我在数据库里重命名状态字段,或者在 CustomBase 里重命名状态属性,这样是可以实现的,但我希望能避免这样做,因为它们都是同一个东西的不同表示,而且在代码中没有必要直接通过枚举值来访问。

谢谢!

1 个回答

1

你最好的选择可能是创建一个自定义的列类型,这样可以把枚举(Enum)转换成你自己的状态类(Status),反之亦然。想了解更多,可以查看这里的详细信息。下面是你core模块的一个草稿,具体的代码会根据你的情况有所不同。

# core module

import sqlalchemy.types as types

class DBStatus (types.TypeDecorator):

    impl = types.Enum

    # what should happen with Status objects on the way into the table
    def process_bind_param(self, value, dialect):
        if value is None:
            return value
        return str(value)  # if Status has a __str__ or __repr__ method

    # what should happen with Enum objects on the way out of the table
    def process_result_value(self, value, dialect):
        if value is None:
            return value
        return Status(value)

foo_table = Table(
    'foo',
    MetaData(),
    Column('status', DBStatus('OK', 'Error')),
    # ...
)

在这之后,你在映射的模块里就不需要做任何特别的事情了:

# module with the mappings

Base = declarative_base()

class Foo (Base):
    __table__ = foo_table
    # ...

实际上,这样做非常简单,关于状态列(Status columns),你完全可以使用完整的声明式映射(full declarative mapping)。

# everything in one module

class DBStatus (types.TypeDecorator):

    # same as above

Base = declarative_base()

class Foo (Base):
    status = Column(DBStatus('OK', 'Error'))
    # ...

撰写回答