Flask SQLAlchemy数据映射器与主动记录模式的比较

22 人关注

我最近开始研究Flask和Flask-SQLAlchemy。我有Django背景,发现Flask-SQLAlchmey相当复杂。我读到SQLAlchemy实现了数据映射器模式,而Django ORM是基于Active Record模式的。

Here 是写的一个示例代码,它实现了存储库模式来访问数据库。

Here 另一个链接是S.Lott(271k reputation)的评论,他说ORM是数据访问层,它与模型是分开的。

我的问题是这样的。

  • Can you provide a practical use case in the above example or an example of your own where Data mapper pattern is useful? Everywhere I have read is that data mapper pattern is useful in complex situation, but not seen examples.
  • Is using repositories pattern as in above case same as using a data mapper pattern?
  • Does data mapper advocates write select queries in a different class than the model as done in the example?
  • Why is Question.query.filter_by(text = text).all() not better to use than db.session.query(Question).filter(Question.text == text).all() ?
  • 这不是一个重复的 DataMapper与ActiveRecord模式 因为这只是讲述了定义,我对实际的例子更感兴趣。

    2 个评论
    我建议你阅读这本书( amazon.com.br/... )从第44页,马丁-福勒来证明在Java中的适用性。目标DataMapper是:它是为了让目标状态远离持久性目标的状态,以及一个更纯粹的对象
    我想,在行动中的一个有趣的映射例子是 继承等级制度 .不知道你如何用AR实现例如连接表的继承,尽管我对AR不是很了解。
    python
    design-patterns
    activerecord
    flask-sqlalchemy
    datamapper
    Anubhav Agarwal
    Anubhav Agarwal
    发布于 2017-02-25
    3 个回答
    9000
    9000
    发布于 2019-04-16
    已采纳
    0 人赞同

    Point by point.

    我有一个遗留的数据库,我必须为它写一些数据处理的工具。使用Mapper模式,没有ORM/ActiveRecord风格,使我在写查询时和ActiveRecord一样容易。它是在漂亮的可组合对象上操作的,类似于SQL条款,屏蔽了SQL注入。

    对象是 "被动 "的,允许更多的灵活性/统一性:复杂连接的结果是一个命名的元组,正如一个简单选择的结果一样。没有需要关心的身份,没有具有相同身份的缓存对象。

    所有的更新都是明确的;不是 "保存 "一些在其他地方改变的状态,没有钩子在 .save() 上运行,等等。这使得高效的批量更新变得微不足道,而不用担心是否有正确的数据被发送到数据库中。在我的案例中,这两点都是好处。在一般情况下,"这取决于"。例如,我不得不在插入后手动获取数据库生成的ID。明确地运行这个查询是一个有点额外的工作。在我的情况下,能够在一次查询中完成,而不是每条记录一次,是一个巨大的福音。

    SQLAlchemy有一个分层设计,允许你访问较低的 "映射器 "层,即使你在上层ORM层声明东西,并且通常在上面操作。例如,在Django中,如果/如果仍有可能的话,这就不那么直接了当了。

    在这个例子中,"资源库 "看起来像一个建好的级别 以上 the 'mapper'. The repository could have been built on top of plain DBAPI, but the mapper makes a few things simpler, like nicer parameter binding, named tuples for the result sets, and a wrapper 以上 plain SQL with composable, reusable parts.

    映射器还提供了一定程度的数据库独立性。例如,SQL Server和Postgres有不同的方法来连接字符串;映射器提供了一个统一的接口。

    你把你的 select 写在你使用它的地方。如果你有一个在不同环境下不断重复使用的选择,你可以把它放到一个方法或函数中。大多数选择都有一个用途,并且是当场建立的。

    SQLAlchemy设计的一个很好的特点是,你可以很容易地存储条件和整个 where 子句,并在选择/更新/删除语句中重复使用它们。

    Question.query.filter_by(text = text).all() 使用隐式交易。 db.session.query(Question).filter(Question.text == text).all() 使用显式事务。

    明确的事务让你对DML感到放心。它们对 select 也很重要,当你查询一个快速变化的数据库,并希望你的几个相关的 select 看到相同的一致状态。

    我通常在 sessionmaker 周围写一个微不足道的包装器,并这样写东西。

    with my_database.transaction() as trans:
       records = trans.query(...)
       updated = trans.execute(...).rowcount
    # Here the transaction commits if all went well.
    

    当我确定知道没有任何DML应该在这个块中运行时,我使用.readonly_transaction(),它总是回滚。

    在很多情况下,隐式事务是可以的。Django允许你用@transaction.atomic来装饰一个方法,并有一个半显式的事务控制,在99%的情况下足够了。但有时你需要更精细的粒度。

    在第4点中, db.session 是不是就是由 "我 "使用的线程局部范围的会话? 替换代码1】属性速记法 ?
    意味着它们共享同一个事务,无论是显式处理还是隐式处理(请求范围)。
    Alexander Litvinenko
    Alexander Litvinenko
    发布于 2019-04-16
    0 人赞同

    完全同意上面的回答:是的,SQLAlchemy的数据映射器模式确实更灵活,对于复杂的查询来说,它确实更强大,不那么神奇,更容易控制。

    但是,在简单的任务中,如 CRUD SQLAlchemy的代码变得过重/过大/过剩。

    例如,如果只是在最简单的 "创建 "控制器中创建一些对象,你需要这样做。

    user = User(name='Nick', surname='Nickson')
    session.add(user)
    session.flush()
    

    而在Active Record ORM中,你只需要一个字符串。

    Well, 用于简单的任务而我们中的一些人可能想要更简单的东西。 我的意思是,为SQLAlchemy提供Active Record将是一件很酷的事情。

    好消息:我最近为此创建了软件包(它还包含其他有用的东西)。

    Check it out: https://github.com/absent1706/sqlalchemy-mixins

    谢谢你分享你的混合项目!很有 "轨":)
    spedy
    spedy
    发布于 2019-04-16
    0 人赞同