SQLAlchemy Reflection Using Metaclass with Column Override

好久不见. 提交于 2019-12-11 09:18:14

问题


I have a set of dynamic database tables (Postgres 9.3 with PostGIS) that I am mapping using a python metaclass:

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

where, db.Model is the db object via flask-sqlalchemy and tablename is a bit of unicode. The cls is then added to an application wide dictionary current_app.class_references (using Flask's current_app) to avoid attempts to instantiate the class multiple times.

Each table contains a geometry column, wkb_geometry stored in Well Known Binary. I want to map these to use geoalchemy2 with the final goal of retrieving GeoJSON.

If I was declaring the table a priori, I would use:

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

Since I am trying to do this dynamically, I need to be able to override the reflection of cls1 with the known type.

Attempts:

  1. Define the column explicitly, using the reflection override syntax.

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

which returns the following on a fresh restart, i.e. the class has not yet been instantiated: InvalidRequestError: Table 'tablename' is already defined for this MetaData instance. Specify 'extend_existing=True' to redefine options and columns on an existing Table object

  1. Use mixins with the class defined above (sans tablename):

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

Again MetaData issues.

  1. Override the column definition attribute after the class is instantiated:

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

Which results in:

InvalidRequestError: Implicitly combining column tablename.wkb_geometry with column tablename.wkb_geometry under attribute 'wkb_geometry'. Please configure one or more attributes for these same-named columns explicitly.

Is it possible to use the metadata construction to support dynamic reflection **and* *override a column known will be available on all tables?


回答1:


I'm not sure if I exactly follow what you're doing, but I've overridden reflected columns in the past inside my own __init__ method on a custom metaclass that inherits from DeclarativeMeta. Any time the new base class is used, it checks for a 'wkb_geometry' column name, and replaces it with (a copy of) the one you created.

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})

This may not exactly work for you, but it's an idea. You probably need to add db.Model to the MyBase tuple, for example.




回答2:


This is what I use to customize a particular column while relying on autoload for everything else. The code below assumes an existing declarative Base object for a table named my_table. It loads the metadata for all columns but overrides the definition of a column named polygon:

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

Other arguments to the Table constructor can be provided in the dictionary. Note that the dictionary must appear last in the list!

The SQLAlchemy documentation Using a Hybrid Approach with __table__ provides more details and examples.



来源:https://stackoverflow.com/questions/26063424/sqlalchemy-reflection-using-metaclass-with-column-override

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!