在SQLAlchemy 0.7中使用带有缓存元数据的延迟反射

时间:2015-05-15 10:00:29

标签: postgresql python-2.7 sqlalchemy

在我的项目中,正在使用具有延迟反射的delaretive base。

from sqlalchemy import (
    create_engine, Table, Column, Integer, BigInteger, Text, DateTime, Numeric, String,
)

from sqlalchemy.dialects.postgresql import CIDR
from sqlalchemy.orm import mapper, sessionmaker, relationship, backref
from sqlalchemy.orm.mapper import configure_mappers
from sqlalchemy.orm.util import _is_mapped_class
from sqlalchemy.pool import NullPool, QueuePool
from sqlalchemy.ext.declarative import declarative_base, declared_attr
from sqlalchemy.schema import Sequence
from sqlalchemy.ext.hybrid import hybrid_property


# this class is taken from sqlalchemy recipes
class DeclarativeReflectedBase(object):

    _mapper_args = []

    @classmethod
    def __mapper_cls__(cls, *args, **kw):
        cls._mapper_args.append((args, kw))

    @classmethod
    def prepare(cls, engine):
        while cls._mapper_args:
            args, kw = cls._mapper_args.pop()
            klass = args[0]

            if args[1] is not None:
                table = args[1]
                Table(table.name,
                      cls.metadata,
                      extend_existing=True,
                      autoload_replace=False,
                      autoload=True,
                      autoload_with=engine,
                      schema=table.schema)

            for c in klass.__bases__:
                if _is_mapped_class(c):
                    kw['inherits'] = c
                    break

            klass.__mapper__ = mapper(*args, **kw)


Base = declarative_base()


class Reflected(DeclarativeReflectedBase, Base):

    __abstract__ = True


class Customer(Reflected):

    @declared_attr
    def __tablename__(cls):
        return "customer"

    __table_args__ = {"schema": "schema_customer"}

    id = Column('id', Integer, primary_key=True)
    balances = relationship(lambda: Balance, backref="customer")


class Balance(Reflected):

    @declared_attr
    def __tablename__(cls):
        return "balance"

    __table_args__ = {"schema": "billing"}

    id = Column('id', Integer, primary_key=True)
    currency = relationship(lambda: Currency)

    @hybrid_property
    def name_with_currency(self):
        return "{0} ({1})".format(self.name, self.currency.name_short)

    @name_with_curr.expression
    def name_with_curr_expr(cls):
        return cls.name + " (" + Currency.name_short + ")"

.......

# and other classes below


def orm_initialization(engine):
    Reflected.prepare(engine)  # reflect DB tables metadata

    S = sessionmaker()
    s = S(bind=engine)

    try:
        contetxs_count = s.query(Contexts).count()

        # other classes difine for not constant number of postgresql database contexts
        ....
        # call prepare again
        Reflected.prepare(engine)
    finally:
        s.close()


def sessionmaker_get(host, port, dbname, user, password, existing_engine=None, pooling=False):
    if existing_engine is None:
        engine = create_engine(
            "{dialect}://{user}:{password}@{host}:{port}/{dbname}"
            .format(dialect="postgresql", **locals()),
            poolclass=NullPool,
            echo=False
        )
    else:
        engine = existing_engine

    orm_initialization(engine)

    S = sessionmaker(bind=engine)
    return S

这可行,但ORM的初始化大约需要7-8秒。所以,我想在一些文件中缓存pickled元数据并在创建新的sessionmaker时加载它。我将元数据存储到HDD上的文件中,并尝试在首次调用prepare()之前将此元数据连接到Reflected。

def orm_initialization(engine):

    import cPickle
    with open('/tmp/metadata') as meta_file:
        meta = cPickle.load(meta_file)
        meta.bind = engine
    Reflected.metadata = meta

    Reflected.prepare(engine)  # reflect DB tables metadata

    S = sessionmaker()
    s = S(bind=engine)
    ....

但后来我收到错误

ArgumentError:无法确定关系上父/子表之间的连接条件...指定'primaryjoin'表达式。如果存在'secondary',则还需要'secondaryjoin'。

我做错了什么?如何正确加载pickled元数据?

0 个答案:

没有答案