SQLAlchemy反射使用元数据与列覆盖

时间:2014-09-26 15:46:35

标签: python-2.7 reflection sqlalchemy flask-sqlalchemy geoalchemy

我有一组动态数据库表(Postgres 9.3 with PostGIS),我使用python元类映射:

cls = type(str(tablename), (db.Model,), {'__tablename__':tablename})

其中,db.Model是通过flask-sqlalchemy的db对象,tablename是一个unicode。 然后将cls添加到应用程序范围的字典current_app.class_references(使用Flask的current_app),以避免尝试多次实例化该类。

每个表都包含一个存储在Well Known Binary中的几何列wkb_geometry。我想将这些映射到使用geoalchemy2,最终目标是检索GeoJSON。

如果我以先验方式宣布该表,我会使用:

class GeoPoly():
    __tablename__ = 'somename'
    wkb_geometry = db.Column(Geometry("POLYGON"))
    #more columns...

由于我试图动态地执行此操作,因此我需要能够使用已知类型覆盖cls1的反射。

尝试:

  1. 使用反射覆盖语法显式定义列。

    cls = type(str(tablename), (db.Model,), {'__tablename__':tablename,
        'wkb_geometry':db.Column(Geometry("POLYGON"))})
    
  2. 在重新启动时返回以下内容,即该类尚未实例化: InvalidRequestError:已为此MetaData实例定义了表'tablename'。指定'extend_existing = True'以重新定义现有Table对象上的选项和列

    1. 将mixins与上面定义的类一起使用(无 tablename ):

      cls = type(str(tablename), (GeoPoly, db.Model), {'__tablename__':tablename})
      
    2. 再次MetaData问题。

      1. 在实例化类后重写列定义属性:

        cls = type(str(tablename), (db.Model,), {'__tablename__':tablename})
        current_app.class_references[tablename] = cls
        cls.wkb_geometry = db.Column(Geometry("POLYGON"))
        
      2. 结果是:

        InvalidRequestError:将列tablename.wkb_geometry与列tablename.wkb_geometry隐式组合在属性'wkb_geometry'下。请明确为这些同名列配置一个或多个属性。

        是否可以使用元数据结构来支持动态反射**和* *覆盖已知的列将在所有表上使用?

2 个答案:

答案 0 :(得分:0)

我不确定我是否完全遵循您正在做的事情,但我已经在我自己的__init__方法中覆盖了过去自定义元类的反映列。 DeclarativeMeta。无论何时使用新的基类,它都会检查“wkb_geometry”'列名称,并将其替换为您创建的名称的副本。

import sqlalchemy as sa
from sqlalchemy.ext.declarative import DeclarativeMeta, declarative_base

wkb_geometry = db.Column(Geometry("POLYGON"))

class MyMeta(DeclarativeMeta):
    def __init__(cls, clsname, parents, dct):
        for key, val in dct.iteritems():
            if isinstance(sa.Column) and key is 'wkb_geometry':
                dct[key] = wkb_geometry.copy()

MyBase = declarative_base(metaclass=MyMeta)

cls = type(str(tablename), (MyBase,), {'__tablename__':tablename})

这可能不适合你,但这是一个想法。例如,您可能需要将db.Model添加到MyBase元组中。

答案 1 :(得分:0)

这是我用来自定义特定列,同时依靠autoload进行其他操作的方式。以下代码假定名为Base的表已有一个声明性my_table对象。它会加载所有列的元数据,但会覆盖名为polygon的列的定义:

class MyTable(Base):
    __tablename__ = 'my_table'
    __table_args__ = (Column(name='polygon', type=Geometry("POLYGON"),
                      {'autoload':True})

可以在字典中提供Table构造函数的其他参数。请注意,字典必须出现在列表的最后!

SQLAlchemy文档Using a Hybrid Approach with __table__提供了更多详细信息和示例。