-
-
Save jvanasco/1556734 to your computer and use it in GitHub Desktop.
import logging | |
log = logging.getLogger(__name__) | |
from sqlalchemy import Table | |
from sqlalchemy import MetaData | |
from sqlalchemy.orm import mapper | |
from sqlalchemy.orm import scoped_session | |
from sqlalchemy.orm import sessionmaker | |
from zope.sqlalchemy import ZopeTransactionExtension | |
import app | |
import types | |
DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension())) | |
DBMetadata = MetaData() | |
class ReflectedTable(object): | |
"""Base class for database objects that are mapped to tables by reflection. | |
Have your various model classes inherit from this class. If class.__tablename__ is defined, it will reflect | |
Example: | |
class Useraccount(ReflectedTable): | |
__tablename__ = "useraccount" | |
""" | |
__tablename__ = None | |
def map_tables( app_model ): | |
""" | |
""" | |
to_reflect = [] | |
for content in dir( app_model ): | |
module = getattr( app_model , content ) | |
if not isinstance( module , types.ModuleType ): | |
continue | |
for module_element in dir( module ): | |
module_element = getattr( module, module_element ) | |
if not isinstance( module_element , types.TypeType ): | |
continue | |
if issubclass( module_element , ReflectedTable ): | |
to_reflect.append( module_element ) | |
for _class in to_reflect: | |
table_name = _class.__tablename__ | |
if table_name: | |
log.info("Reflecting : %s (table: %s)" % (_class , table_name) ) | |
table= Table( table_name, DBMetadata, autoload=True ) | |
mapper( _class , table) | |
def initialize_sql(engine): | |
"""Call this once per engine from app.__init__.main | |
engine = sqlalchemy.engine_from_config(settings, prefix="sqlalchemy.") | |
sqlahelper.add_engine(engine) | |
models.initialize_sql(engine) | |
""" | |
log.debug( "\ninitialize_sql" ) | |
DBSession.configure(bind=engine) | |
DBMetadata.bind = engine | |
map_tables( app.models ) | |
## import various classes here. | |
import models_1 | |
import models_2 | |
import models_3 |
yeah, the introspection is a bit ugly.
the reason why i decided to do it that way is that i've been refactoring a bunch of legacy code that i used as a 'bootstrap' module under pylons into pyramid - and splitting out the database portion of it
for the database code, i want to:
a_ reflect tables
b_ setup and manage multiple db handles ( read, write, log, etc )
right now, it looks more like this:
https://gist.github.com/1686834
ideally when I'm done , i'd do something like this:
- deploy the final version to github or pypi & have my apps require it
- drop a line into app/init.py to push some setup stuff into app/models/init.py
- models/init.py just sets up the handles , and calls a master init in my package (which then does introspection)
i have at least 6 pylons apps that i'm migrating to pyramid, and am in the process of building 3 apps right now -- the more I can take stuff like this and abstract/simplify/standardize, the happier i am. the introspection is ugly, but its ultimately easier to migrate & easier to maintain.
For future readers.. The DeferredReflection mixin is now built in to sqlaclehemy and should remove the need for special handling.
See docs here: http://docs.sqlalchemy.org/en/rel_0_9/orm/extensions/declarative.html#using-reflection-with-declarative
I like your idea of having
__tablename__
as a class attribute ofReflectedTable
. You've also made me realize that there's no good reason for themap_by_reflection
class method. But your introspection code inmap_tables
to tease theReflectedTable
classes out ofapp_model
doesn't thrill me.In my present app all of the model classes are reflected tables, so I can't see the value in putting them in another module. I may think differently if I do something where I have a mixture of reflected table models and app-specific ones. So, for now I've opted to keep everything in
models.py
, refactored to: