2016-08-19 29 views
15

私のFlask-Restfulアプリケーションには多数の「オブジェクト」があります。アプリの最初のバージョンでは、これは動作のない単純なデータ構造 、Dictsとして実装されたもの、またはDictsのリストです。SQLAlchemyオブジェクトインスタンスと属性の等価性の比較

これらの「オブジェクト」の属性は変更される可能性があります。私はジェネレータ機能を使用して変更を追跡し、サーバー送信イベント(SSE)を介してWebクライアントに警告します。 これは、追跡されるオブジェクトの「古い」コピーを維持し、それを最新の状態と比較することによって機能します。

次のバージョンのアプリケーションでは、SQLAlchemyを使用してSQLite DBから「オブジェクト」を作成します。 オブジェクトはSQLAlchemyの宣言的なクラスまたはそのようなクラスのリストとして実装されるようになりました。

属性の同等性に基づいて「古い」インスタンスと「新しい」インスタンスを比較するには、 SQLAlchemyオブジェクトに__eq__のオーバーライドを追加する必要がありました。すなわち、属性が同じ値を有するとき、インスタンスは等しい/不変であると見なされる。 (私はこの質問の一番下にサンプルコードを掲載しています)。

これは技術的には機能しますが、アーキテクチャ上の警鐘を喚起します。誤った方向に航行していますか?私はSQAlchemyオブジェクトに__eq____ne__オーバーライドを追加する場合

a)は、この原因は、私は後で が戻ってデータベースにオブジェクトを再永続化したい問題をSQLAlchemyのだろうか?

b)SQLAlchemyオブジェクトに到達するまでには、どのくらいの時間が必要ですか?「Pythonicベストプラクティス」はありますか?つまり、SQLAlchemyオブジェクトをビジネスロジック/ビヘイビア(DBパーシステンスとの接続が変更されていないなど)に拡張することはOK /正常ですか?または、データベースとサーバー間の単純なDTOとしてのみ使用し、他のオブジェクトにビジネスロジックを使用する必要がありますか?

注:REST APIおよびSSEを介してクライアントに提示されるデータは、WebサーバーおよびDBの実装の詳細から抽象化する必要があるため、この質問の一部ではありません。 https://codereview.stackexchange.com/questions/93511/data-transfer-objects-vs-entities-in-java-rest-server-application http://www.mehdi-khalili.com/orm-anti-patterns-part-4-persistence-domain-model/

sqlalchemy id equality vs reference equality

class EqualityMixin(object): 
# extended from the concept in : 
# https://stackoverflow.com/questions/390250/elegant-ways-to-support-equivalence-equality-in-python-classes 

    def __eq__(self, other): 
     classes_match = isinstance(other, self.__class__) 
     a, b = deepcopy(self.__dict__), deepcopy(other.__dict__) 
     #compare based on equality our attributes, ignoring SQLAlchemy internal stuff 
     a.pop('_sa_instance_state', None) 
     b.pop('_sa_instance_state', None) 
     attrs_match = (a == b) 
     return classes_match and attrs_match 

    def __ne__(self, other): 
     return not self.__eq__(other) 

答えて

1

私は__eq____ne__上書きがうまくあることを示すために、基本クラスの背後に何が起こっているかをドリルダウンします。 クラスをインスタンス化するときには、declarative_base()を呼び出すことでメタセラスを使用して設定します(この理由を理解するには、this metaclass explanationを読む価値があります)。 Baseクラスにカスタムコンストラクタを追加し、オブジェクトからテーブルへのマッピング方法を設定するなど、いくつかの設定可能な設定を行います。

declarative_base()は、新しいBaseクラスインスタンスのDeclarativeMetaメタクラスを返します。あなたがBaseを拡張するクラスを作成するときに、それがテーブルにマップされるように、メタクラスがここで関与する理由のすべてが含まれています。このパスを少しトレースすると、オブジェクト上で宣言したColumnをテーブルにどのようにマップするかがわかります。

self.cls.__mapper__ = mp_ = mapper_cls(
     self.cls, # cls is your model 
     self.local_table, 
     **self.mapper_args # the columns you have defined 
    ) 

、それは非常に複雑で低レベルを取得するようにこれをやっている実際のマッパーが見えますが、この段階ではそれが主キーと列ではなく、実際のオブジェクトのインスタンスで動作しています。しかしこれは決して使用されていないことを確認していないので、ソースで==!=の使用法を見ていて、何の懸念も見られませんでした。

あなたの2番目の質問については、私は自分の意見を実際に提供することができます - 過去に何度もこの話題を捜したことがありますが、「金標準」のSQL Alchemyの使い方はあまり見当たらません。私は今までのいくつかのプロジェクトでSQL Alchemyを使用しましたが、オブジェクトの使用は、sessionのライフサイクルを分かりやすく抽象化することができる範囲で可能です。私にとっては、Alchemyの「魔法」がモデル自体から抽象化されているように思えます。セッションがうまく処理されると、データレイヤーから十分に遠く離れているため、クラス内のビジネスロジックが邪魔になるような気がしません道。

関連する問題