Можно ли выгрузить декларативные классы в SQLAlchemy? - PullRequest
9 голосов
/ 03 марта 2011

Я работаю над библиотекой, в которой пользователь сможет просто объявить несколько классов, которые автоматически поддерживаются базой данных. Короче, где-то спрятано в коде, там есть

from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class LibraryBase(Base):
    # important library stuff

и пользователь должен затем сделать

class MyStuff(LibraryBase):
    # important personal stuff

class MyStuff_2(LibraryBase):
    # important personal stuff

mystuff = MyStuff()
Library.register(mystuff)
mystuff.changeIt() # apply some changes to the instance
Library.save(mystuff) # and save it

# same for all other classes

В статической среде, например пользователь создал один файл со всеми личными классами и импортирует этот файл, это работает довольно хорошо. Все имена классов фиксированы, и SQLAlchemy знает, как отобразить каждый класс.

В интерактивной среде все по-другому: теперь есть шанс, что класс будет определен дважды. Оба класса могут иметь разные модули; но все же SQLAlchemy будет жаловаться:

SAWarning: имя класса 'MyStuff' уже есть в реестре этой декларативной базы, сопоставленной с

Есть ли способ справиться с этим? Можно ли как-нибудь выгрузить класс из declarative_base, чтобы я мог поменять его определение на новое?

Ответы [ 3 ]

2 голосов
/ 03 декабря 2012

Вы можете использовать:

sqlalchemy.orm.instrumentation.unregister_class(cl)
del cl._decl_class_registry[cl.__name__]

Первая строка - предотвратить случайное использование вашего незарегистрированного класса. Второе отменяет регистрацию и предотвратит предупреждение.

2 голосов
/ 08 марта 2011

Похоже, и я не совсем уверен, что это даже работает, но я думаю, что вы хотите, чтобы

sqlalchemy.orm.instrumentation.unregister_class()

http://hg.sqlalchemy.org/sqlalchemy/file/762548ff8eef/lib/sqlalchemy/orm/instrumentation.py#l466

0 голосов
/ 10 июня 2013

В моем проекте я использую это решение. Там, где в библиотеке указаны столбцы, определенные как mixin declared_attr, а целевой преобразователь, созданный type, вызывается с базами, в результате у меня есть полнофункциональный преобразователь.

from sqlalchemy import create_engine, BigInteger, Column
from sqlalchemy.orm import sessionmaker, scoped_session
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.declarative import declared_attr


Base = declarative_base()


class LibraryBase(object):
    __tablename__ = 'model'

    @declared_attr
    def library_field(self):
        return Column(BigInteger)


class MyLibrary(object):

    @classmethod
    def register(cls, entity):
        tablename = entity.__tablename__
        Mapper = type('Entity_%s' % tablename, (Base, LibraryBase, entity), {
            '__tablename__': tablename,
            'id': Column(BigInteger, primary_key=True),
        })
        return Mapper

    @classmethod
    def setup(cls):
        Base.metadata.create_all()


class MyStaff(object):
    __tablename__ = 'sometable1'

    @declared_attr
    def staff_field(self):
        return Column(BigInteger)

    def mymethod(self):
        print('My method:', self)


class MyStaff2(MyStaff):
    __tablename__ = 'sometable2'


if __name__ == '__main__':
    engine = create_engine('sqlite://', echo=True)
    Base.metadata.bind = engine
    Session = scoped_session(sessionmaker(bind=engine))
    session = Session()

    # register and install
    MyStaffMapper = MyLibrary.register(MyStaff)
    MyStaffMapper2 = MyLibrary.register(MyStaff2)
    MyLibrary.setup()

    MyStaffMapper().mymethod()
    MyStaffMapper2().mymethod()

    session.query(MyStaffMapper.library_field) \
        .filter(MyStaffMapper.staff_field != None) \
        .all() 
...