简体   繁体   中英

Sqlalchemy: Mapping a class to different tables depending on attribute

I'm trying to write to an existing database consisting of multiple tables of the following form

total_usage_<application>:
    id
    version
    date

where <application> runs over a number of strings, say "appl1", "appl2" etc. Now I would like to use SQLAlchemy to create a single class like

class DBEntry:
    id = ''
    application = ''
    version = ''
    date = ''

such that an instance foo of DBEntry gets mapped to the table "total_usage_" + foo.application . How can this be achieved?

OK, please take a look at example below, which is self-contained and might show you one way this can be done. It assumes that you know the app_name when you start your program, as well as it assumes that table names follow some naming convention, which obviously you can adapt to your needs or have it completely manually configured by overriding __tablename__ in each mapped class.
But the main idea is that the configuration is wrapped into a function (which can be done also using module import with pre-defined constant beforehand).

from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship, scoped_session, sessionmaker

def camel_to_under(name):
    import re
    s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
    return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()

def configure_database(app_name):
    """ @return: dictionary with all the classes mapped to proper tables for
    specific application.  """
    from sqlalchemy.ext.declarative import declared_attr
    from sqlalchemy.ext.declarative import declarative_base

    class Base(object):
        # docs: http://docs.sqlalchemy.org/en/rel_0_9/orm/extensions/declarative.html#augmenting-the-base
        @declared_attr
        def __tablename__(cls):
            return camel_to_under(cls.__name__) + "_" + app_name

        def __repr__(self):
            attrs = class_mapper(self.__class__).column_attrs # only columns
            # attrs = class_mapper(self.__class__).attrs # show also relationships
            return u"{}({})".format(self.__class__.__name__,
                ', '.join('%s=%r' % (k.key, getattr(self, k.key))
                    for k in sorted(attrs)
                )
            )

    Base = declarative_base(cls=Base)

    class Class1(Base):
        id = Column(Integer, primary_key=True)
        name = Column(String)

    class Class1Child(Base):
        id = Column(Integer, primary_key=True)
        parent_id = Column(Integer, ForeignKey(Class1.id))
        name = Column(String)
        # relationships
        parent = relationship(Class1, backref="children")

    # return locals()
    return {
        "app_name": app_name,
        "Base": Base,
        "Class1": Class1,
        "Class1Child": Class1Child,
        }

def _test():
    """ Little test for the app.  """
    engine = create_engine(u'sqlite:///:memory:', echo=True)
    session = scoped_session(sessionmaker(bind=engine))

    app_name = "app1"
    x = configure_database(app_name)

    # assign real names
    app_name = x["app_name"]
    Base = x["Base"]
    Class1 = x["Class1"]
    Class1Child = x["Class1Child"]

    # create DB model (not required in production)
    Base.metadata.create_all(engine)

    # test data
    cc = Class1Child(name="child-11")
    c1 = Class1(name="some instance", children=[cc])
    session.add(c1)
    session.commit()

_test()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM