将SQLAlchemy ORM分离的实体作为域对象公开

2024-10-03 06:26:10 发布

您现在位置:Python中文网/ 问答频道 /正文

这是一个相当长的帖子,所以我把它分为主要问题,一个有代码的说明性示例,最后还描述了我的想法和问题。在

问题

我在整个应用程序中使用分离的实体。这是向应用程序的其余部分公开ORM实体的正确方法吗?如果没有,有什么问题?在

代码(设置)

数据库设置:

db_conn_string = "sqlite://"
# db_conn_string = "mysql+pymysql://root:root@33.33.33.1:33060/alchemist"
engine = create_engine(db_conn_string)
Base.metadata.bind = engine
DBSession = sessionmaker(bind=engine, expire_on_commit=False)

对于实体,我只定义关系,如果我急于加载它们。这是为了确保当它们被用作分离的实体时,不能直接检索未加载的相关实体。在

实体(截断):

^{pr2}$

我提供了一个repository对象来执行基本的CRUD操作。然后在存储库周围的上下文对象中管理会话事务范围:

class RepositoryContext(object):
    def __init__(self, entity_class):
        self.entity_class = entity_class

    def __enter__(self):
        self.session = get_session()
        return CrudRepository(self.entity_class, self.session)

    def __exit__(self, exc_type, exc_val, exc_tb):
        try:
            self.session.commit()
        except Exception:
            self.session.rollback()
            raise
        finally:
            self.session.close()


class CrudRepository(object):
    """
    CrudRepository implements (entity agnostic) CRUD operations.
    """

    def __init__(self, entity_class, session):
        self.entity_class = entity_class
        self._session = session

    def retrieve_all(self):
        return self._session.query(self.entity_class).all()

    def retrieve_by(self, **kwargs):
        return self._session.query(self.entity_class).filter_by(**kwargs).one_or_none()

    # Other CRUD methods here.

    def query(self):
        '''To perform custom queries'''
        return self._session.query(self.entity_class)

代码(用法)

所以基本用法如下:

with RepositoryContext(Tree) as repo:
    tree = repo.retrieve_by(id=1)
# Session is closed outside of context. tree is a detached instance.
len(tree.branches)  # This is ok, eager loaded

如果您想获得某个分支的叶子,我们不能使用tree.branches[0].leaves来访问它,因为没有定义该关系。因为我们不想急于加载它,我们必须单独检索它,如下所示:

with RepositoryContext(Leaf) as repo:
    branch = tree.branches[0]
    leaves = repo.query().filter_by(branch_id=branch.id).all()
# Now have leaves related to first branch. Also detached

同样,对于更新或刷新,您需要打开一个上下文并调用相关函数:

with RepositoryContext(Tree) as repo:
    repo.refresh(tree)  # This refreshes the tree state (discarding changes)

上下文

对于一个新项目,我们正在使用sqlalchemyorm,但我不确定如何正确地使用ORM实体。在

我一直被教导域模型和数据库细节应该尽可能地相互分离。然而,对于ORM,这意味着我必须不断地将ORM实体映射到领域模型对象,然后再映射回来,这就破坏了使用ORM的全部意义。在

在应用程序中使用ORM实体的过程中,我想确保不会产生意外的副作用。因此,我创建了一个严格的事务作用域,以强制任何持久性都由存储库类显式处理。这意味着我在整个应用程序中使用分离的实体,并且只在事务中附加它们。在

在我当前的实现中,我失去了SQLAlchemy提供的一些功能。即访问尚未预先加载的相关数据(例如,在代码示例中:branch.leaves未定义,因此您必须通过指定分支id来添加它们)。在

很抱歉,如果这篇文章很长,我已经尽可能多的删节,同时保持它的运行。在


Tags: 代码self实体branchtree应用程序sessiondef