如何使用反射和描述性语法覆盖sqlalchemy中的列名

时间:2011-10-06 20:09:24

标签: python sqlalchemy

您好我正在尝试使用sqlalchemy将遗留应用程序移植到python。

应用程序的现有数据库有大约300个表,每个表中都有一个名为def的列,如:

create table accnt (
    code varchar(20)
  , def varchar(50) --for accnt definition
  , ...
)

因此,当使用声明性语法和反射时,我可以轻松地创建我的类:

class Accnt(Base):
    __table__ = Table('accnt', metadata, autoload = True, autoload_with=engine)

但是当我尝试访问 def 列时,我最终会收到错误消息。例如:

q = session.query(Accnt)
for row in q:
    print q.def

因为 def 是python的保留字:(

为了克服这个问题,我可以创建我的课程:

class Accnt(Base):
    __table__ = Table('accnt', metadata, autoload = True, autoload_with=engine)
    __mapper_args__ = {'column_prefix':'_'}

但是在每个列名前放一个_是无聊的,而不是花哨的。

我想要做的是使用其他名称/(密钥?)访问def列。

有什么想法吗?

---编辑--- (根据TokenMacGuy的要求编辑原始帖子)

虽然我接受了TokenMacGuy的答案,但我之前尝试过:

engine = create_engine('firebird://sysdba:masterkey@127.0.0.1/d:\\prj\\db2\\makki.fdb?charse‌​t=WIN1254', echo=False) 
metadata = MetaData() 
DbSession = sessionmaker(bind=engine) 
Base = declarative_base() 

class Accnt(Base):
    __table__ = Table('accnt', metadata, autoload = True, autoload_with=engine) 
    _def = Column("def", String(50))

而且我有     sqlalchemy.exc.ArgumentError:指定表时无法添加其他列'def' 误差..

我和TokenMacGuy之间的主要区别是

mine       : _table_ ....
TokenMcGuy : __tablename__ = 'accnt'
             __table_args__ = {'autoload': True}

和元数据绑定......

那么,为什么我之前的尝试产生错误?

3 个答案:

答案 0 :(得分:13)

你也可以吃蛋糕。定义要重命名的列; sqlalchemy将自动推断您未提及的任何列。

>>> from sqlalchemy import *
>>> from sqlalchemy.ext.declarative import declarative_base
>>> 
>>> engine = create_engine("sqlite:///:memory:")
>>> 
>>> engine.execute("""
... create table accnt (
...     id integer primary key,
...     code varchar(20),
...     def varchar(50)
... )
... """)
<sqlalchemy.engine.base.ResultProxy object at 0x2122750>
>>> 
>>> Base = declarative_base()
>>> 
>>> Base.metadata.bind = engine
>>> 
>>> class Accnt(Base):
...     __tablename__ = 'accnt'
...     __table_args__ = {'autoload': True}
...     def_ = Column('def', String)
... 
>>> Accnt.def_
<sqlalchemy.orm.attributes.InstrumentedAttribute object at 0x2122e90>
>>> Accnt.code
<sqlalchemy.orm.attributes.InstrumentedAttribute object at 0x2127090>
>>> 

编辑:

通过提供__table__参数,您告诉声明性扩展,您已经拥有了一个您想要使用的正确配置的Table。但事实并非如此;您希望在类中使用另一个名称引用def列。通过使用__tablename____table_args__,您可以推迟表的构造,直到您告诉声明如何使用该表为止。如果您已经开始使用__table__,那么就没有优雅的解决方法。您可以提供对列进行别名的property,也可以将列指定为_def = getattr(__table__.c, 'def')

真的,你应该只使用__tablename__;它既方便又灵活,这就是原因的一个很好的例子。

(顺便说一句,最常见的是为备用标识符提供尾随下划线而不是前导下划线,使用def_代替_def;引导下划线通常表示该名称为“私有”或'一个实现细节',如果名称是公开的,但看起来像一个私人名称,它可能会导致更多的混乱)

答案 1 :(得分:3)

您可以这样定义您的表:

mymetadata = MetaData()
Base = declarative_base(metadata=mymetadata)

class Accnt(Base):
    __tablename__ = 'accnt'

    code = Column(String(20))
    def_ = Column(String(50))

答案 2 :(得分:0)

这可能过于蛮力,但另一种方法是使用sqlacodegen库为您的数据库自动生成所有模型,然后手动更改这些模型,或调整sqlacodegen来构建使用您的约定的模型。它支持将保留字映射到其他符号。

https://pypi.python.org/pypi/sqlacodegen。不错的工具。