【问题标题】:SQLAlchemy Reflection Using Metaclass with Column Override使用带有列覆盖的元类的 SQLAlchemy 反射
【发布时间】:2014-11-21 16:37:50
【问题描述】:

我有一组动态数据库表(带有 PostGIS 的 Postgres 9.3),我正在使用 python 元类进行映射:

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

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

每个表都包含一个几何列,wkb_geometry 存储在 Well Known Binary 中。我想将这些映射到使用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"))})
    

在重新启动时返回以下内容,即该类尚未实例化: InvalidRequestError: 表 'tablename' 已为此 MetaData 实例定义。指定 'extend_existing=True' 以重新定义现有 Table 对象上的选项和列

  1. 对上面定义的类使用 mixins(无 tablename):

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

又是元数据问题。

  1. 类实例化后覆盖列定义属性:

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

结果:

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

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

【问题讨论】:

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


    【解决方案1】:

    我不确定我是否完全按照您的操作进行操作,但我过去在继承自 DeclarativeMeta 的自定义元类上覆盖了我自己的 __init__ 方法中的反射列。每当使用新基类时,它都会检查“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 元组中。

    【讨论】:

      【解决方案2】:

      这是我用来自定义特定列的方法,同时依赖 autoload 处理其他所有内容。下面的代码假定一个名为my_table 的表的现有声明性Base 对象。它会加载所有列的元数据,但会覆盖名为 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__ 提供了更多详细信息和示例。

      【讨论】:

        猜你喜欢
        • 2012-08-31
        • 1970-01-01
        • 2010-10-30
        • 2011-12-02
        • 1970-01-01
        • 2023-04-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多