最近、FlaskとFlask-SQLAlchemyの作業を開始しました。Djangoの背景から、Flask-SQLAlchmeyは非常に複雑であることがわかりました。SQLAlchemyが実装していることを読みました。 Django ORMはアクティブレコードパターンに基づいていますが、データマッパーパターン。
Here は、データベースにアクセスするためのリポジトリパターンを実装するために記述されたサンプルコードです。
ここ は、ORMがデータアクセス層であり、モデルとは別であるというS.Lott(271kレピュテーション)によるコメントの別のリンクです。
私の質問はこれらです:
Question.query.filter_by(text = text).all()
をdb.session.query(Question).filter(Question.text == text).all()
よりも使いたくないのはなぜですか?これは DataMapper vs ActiveRecord pattern の複製ではありません。これは定義を示しているだけなので、実際の例にもっと興味があります。
ポイントごとに。
いくつかのデータ処理ユーティリティを作成する必要があるレガシーデータベースがあります。 ORM/ActiveRecordスタイルなしでマッパーパターンを使用すると、ActiveRecordと同じくらい簡単にクエリを作成できます。これは、SQL句に似た、SQLインジェクションから保護されたNice構成可能オブジェクトで動作しています。
オブジェクトが「パッシブ」であるため、柔軟性と均一性が向上します。複雑な結合の結果は、単純な選択の結果と同様に、名前付きタプルになります。気にするIDはなく、同じIDを持つキャッシュされたオブジェクトもありません。
すべての更新は明示的です。他の場所で変更された状態の「保存」ではなく、.save()
で実行されているフックもありません。これにより、適切なデータがDBに送信されても問題なく、効率的なバッチ更新が簡単になりました。私の場合、どちらもメリットでした。一般的に、「それは依存します」。たとえば、挿入後にデータベースで生成されたIDを手動でフェッチする必要がありました。このクエリを明示的に実行することは、少し余分な作業です。私の場合、レコードごとに1つではなく、1つのクエリでそれを実行できることは大きな恩恵でした。
SQLAlchemyは、上位のORMレベルで宣言し、通常はそれを操作する場合でも、下位の「マッパー」レベルにアクセスできるようにする階層化された設計を備えています。たとえば、Djangoでは、可能であれば、それはそれほど単純ではありません。
この例では、「リポジトリ」は構築されたレベルのように見えます上記「マッパー」。リポジトリはプレーンDBAPIの上に構築することもできますが、マッパーを使用すると、パラメーターバインディングの改善、結果セットの名前付きタプル、構成可能で再利用可能なパーツを備えたプレーンSQLのラッパーなど、いくつかのことが簡単になります。
マッパーは、ある程度のデータベースの独立性も提供します。例えば。 SQL ServerとPostgresには、文字列を連結する方法が異なります。マッパーは統一されたインターフェースを提供します。
select
を使用する場所に書き込みます。さまざまなコンテキストで常に再利用するselectがある場合は、それをメソッドまたは関数に入れることができます。ほとんどの選択には1つの用途があり、その場で構築されます。
SQLAlchemyの設計の優れた機能は、条件とwhere
句全体を簡単に保存し、select/update/deleteステートメント全体でそれらを再利用できることです。
Question.query.filter_by(text = text).all()
は暗黙的なトランザクションを使用します。 db.session.query(Question).filter(Question.text == text).all()
は明示的なトランザクションを使用します。
明示的なトランザクションにより、DMLで安心できます。急速に変化するデータベースにクエリを実行していて、関連する複数のselect
sに同じ一貫性のある状態を表示させたい場合は、select
sでも重要です。
私は通常、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%の場合に十分な、半明示的なトランザクション制御を行うことができます。ただし、さらに細かい粒度が必要な場合もあります。
上記の答えに完全に同意します。はい、SQLAlchemyのデータマッパーパターンは本当に柔軟性があり、複雑なクエリの場合は、より強力で、魔法が少なく、より制御されています。
しかし、 [〜#〜] crud [〜#〜] のような単純なタスクでは、SQLAlchemyのコードは太りすぎ/過剰/冗長になります。
たとえば、最も単純な「作成」コントローラーでオブジェクトを作成するには、次のようなものが必要です。
user = User(name='Nick', surname='Nickson')
session.add(user)
session.flush()
Active Record ORMでは、必要な文字列は1つだけです。
さて、単純なタスクの場合、私たちの中にはもっと単純なものが欲しい人もいるかもしれません。 SQLAlchemyのアクティブレコードがあると便利です。
良いニュース:私は最近このためのパッケージを作成しました(他の便利なものも含まれています)。
チェックしてください: https://github.com/absent1706/sqlalchemy-mixins
ActiveRecordではなくDataMapperを使用する唯一の理由は、スケーラビリティに深刻な問題がある場合です。データマッパーはドメインオブジェクトとデータベースアクセスロジックの分離を促進しますが、アクティブレコードはデータベースアクセスロジックをドメインオブジェクトに配置します。たとえば、Flaskインスタンスを持ち上げると、オンデマンドでのみデータベースに接続されますが、Djangoでは、常にデータベースに接続されます。
データマッパーはドメインオブジェクトをデータベースアクセスロジックから分離しますが、リポジトリパターンはドメインオブジェクトとデータマッパーの間のレイヤーです。データマッパーよりも高いレベルです。たとえば、データマッパーパターンには単純なゲッターとセッターがあり、リポジトリパターンには複雑なビジネスロジックを含む可能性のあるゲッターとセッターがあります。
データマッパーはモデルクラスから分離されています。 ActiveRecordパターンのみが同じクラスのゲッターとセッターに参加します。
私はSQLAlchemyとDjangoの両方をしばらく使ってきましたが、Djangoのようなクエリを絶対に好みます。私自身のプロジェクトでは、Flask + SQLAlchemyをDjangoで使用する確率はほとんどありません。これらの2つのフレームワークを検討する場合、生産性とコミュニティが2つの最も決定的な要因です。