SQLAlchemy使用继承来反射列类型

20

考虑以下代码(使用SQLAlchemy 0.7.7):

class Document(Base):
    __tablename__ = 'document'
    __table_args__ = {
        'schema': 'app'
    }

    id = Column(types.Integer, primary_key=True)
    nom = Column(types.Unicode(256), nullable=False)
    date = Column(types.Date())

    type_document = Column(types.Enum('arrete', 'photographie',
        name='TYPES_DOCUMENT_ENUM'))
    __mapper_args__ = {'polymorphic_on': type_document}

class Arrete(Document):
    __tablename__ = 'arrete'
    __table_args__ = {
        'schema': 'app'
    }
    __mapper_args__ = {'polymorphic_identity': 'arrete'}

    id = Column(types.Integer, ForeignKey('app.document.id'), primary_key=True)
    numero_arrete = Column(types.Integer)
    date_arrete = Column(types.Date())

我可以使用以下代码轻松获取在 Arrete 类中定义的列的列类型:

Arrete.__table__.c['date_arrete'].type

如果我想通过Arrete类访问在Document类中定义的列,则这种方法无法使用(如果尝试访问c ['date'],会出现KeyError)。

是否有一种方法可以获取列类型,而不管该列是在最终类中定义还是在其父类之一中定义?

3个回答

33

ORM允许您定义类与两个表JOIN的继承模式相对应。这个结构是全功能的,还可以直接找到基本的事物,例如列上属性的类型:

type = Arrete.date.property.columns[0].type

请注意,这基本上与通过遍历__bases__相同的方法类似,只是你让Python的正常类机制来完成工作。


哦,太干净了!谢谢! :) - tonio
2
@zzzeek,columns属性为什么是一个列表?是否存在第一个实例不正确的情况? - ricekab
和 @ricekab 一样好奇。 - weaming

7

您可以探索基础类...

def find_type(class_, colname):
    if hasattr(class_, '__table__') and colname in class_.__table__.c:
        return class_.__table__.c[colname].type
    for base in class_.__bases__:
        return find_type(base, colname)
    raise NameError(colname)

print find_type(Arrete, 'date_arrete')
print find_type(Arrete, 'date')

我只是将它作为我的基类的@classmethod添加,看起来正如预期的那样工作。非常感谢! - tonio
1
这个答案的问题在于它不必要地穿过__bases__,而仅仅访问Arrete.date就可以为您完成这项工作。当然,SQLAlchemy可以根据映射属性提供列/数据类型。 - zzzeek

0
你需要使用抽象指令或者混入模式
对于混入模式,你可以这样使用:
class MyMixin(object):
    __tablename__ = 'document'
    __table_args__ = {
        'schema': 'app'
    }

    id = Column(types.Integer, primary_key=True)
    nom = Column(types.Unicode(256), nullable=False)
    date = Column(types.Date())

class Arrete(MyMixin, Base):
    __tablename__ = 'arrete'

    __mapper_args__ = {'polymorphic_identity': 'arrete'}

    foreign_id = Column(types.Integer, ForeignKey('app.document.id'), primary_key=True)
    numero_arrete = Column(types.Integer)
    date_arrete = Column(types.Date())


class Document(MyMixin, Base):
    __tablename__ = 'Document'
    type_document = Column(types.Enum('arrete', 'photographie',
        name='TYPES_DOCUMENT_ENUM'))
    __mapper_args__ = {'polymorphic_on': type_document}

共享的内容放在Mixin中,非共享的内容放在子类中。


看起来这会破坏我的应用程序中一些其他代码,其中“Arrete”必须扩展“Document”。我不希望我的示例的确切语法起作用(用于获取列类型),但如果可能的话,我真正想要的是保持数据库不变。 - tonio
如果 Arrete 只是在扩展文档,则数据库上应该只有一个名为 arrete 的表。你无法像你想的那样进行扩展,因为每个 __table__ 都是一个对象,在 metadata 中有一个唯一的条目。尝试在类之间共享 __table__ 对象会防止每个类拥有不同的表。也许你需要重新考虑你的数据库模型。能提供更多细节吗? - Spencer Rathbun
Arrete只是一个例子,我还有更多需要扩展的表格文档。我的应用程序中还有几个带有继承的表格,我不想改变它们。也许我应该更好地解释我的需求,无论如何感谢你的帮助。 - tonio

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接