简体   繁体   English

是否可以在SQLAlchemy中卸载声明性类?

[英]Is it possible to unload declarative classes in SQLAlchemy?

I'm working on a library where the user shall be able to simply declare a few classes which are automatically backed by the database. 我正在开发一个库,用户可以简单地声明一些由数据库自动支持的类。 In short, somewhere hidden in the code, there is 简而言之,隐藏在代码中的某个地方就有了

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

and the user should then do 然后用户应该这样做

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

In a static environment, eg the user has created one file with all personal classes and imports this file, this works pretty well. 在静态环境中,例如用户创建了一个包含所有个人类的文件并导入了该文件,这非常有效。 All class names are fixed and SQLAlchemy knows how to map each class. 所有类名都是固定的,SQLAlchemy知道如何映射每个类。

In an interactive environment, things are different: Now, there is a chance of a class being defined twice. 在交互式环境中,情况有所不同:现在,有可能两次定义一个类。 Both classes might have different modules; 这两个类可能有不同的模块; but still SQLAlchemy will complain: 但SQLAlchemy仍会抱怨:

SAWarning: The classname 'MyStuff' is already in the registry of this declarative base, mapped to < class 'OtherModule.MyStuff' > SAWarning:类名'MyStuff'已经在此声明基础的注册表中,映射到<class'Othermodule.MyStuff'>

Is there a way to deal with this? 有办法解决这个问题吗? Can I somehow unload a class from its declarative_base so that I can exchange its definition with a new one? 我可以以某种方式从其declarative_base 卸载一个类,以便我可以用一个新的交换它的定义吗?

You can use: 您可以使用:

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

The first line is to prevent accidental use of your unregisted class. 第一行是为了防止意外使用您的未注册类。 The second unregisters and will prevent the warning. 第二次注册并将阻止警告。

It looks like, And I'm not really sure this even works, but I think what you want is 它看起来像,我不确定这甚至有效,但我认为你想要的是

sqlalchemy.orm.instrumentation.unregister_class()

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

In my project I use this solution. 在我的项目中,我使用此解决方案。 Where library specified columns defined as mixin by declared_attr and target mapper created by type call with bases, as result I have full functional mapper. 其中库指定的列由declared_attr定义为mixin,而type调用由base创建的目标映射器,因此我有完整的功能映射器。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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