2017-06-13 18 views
0

私たちは、tornadoとsqlalchemyを使用してアプリケーションのサービスを作成しています。アプリケーションはdjangoで書かれ、 "ソフト削除メカニズム"を使用します。つまり、基礎となるmysqlテーブルには削除がなかったということです。行を削除済みとしてマークするには、属性付きの「削除」をTrueに設定するだけです。しかし、サービスでは、sqlalchemyを使用しています。最初は、私たちのようなSQLAlchemyの自身によって作られたクエリで削除のチェックを追加しました:開発者がクエリで削除のチェックを欠場する傾向があるので、sqlalchemyを使用した「ソフト削除」システムの実装

customers = db.query(Customer).filter(not_(Customer.deleted)).all() 

しかし、これは潜在的なバグの多くにつながります。そこで我々は、「プレフィルター」を行い、当社の問合せクラスで問い合わせるデフォルトを上書きすることを決めた:

class SafeDeleteMixin(Query): 
    def __iter__(self): 
     return Query.__iter__(self.deleted_filter()) 
    def from_self(self, *ent): 
     # override from_self() to automatically apply 
     # the criterion too. this works with count() and 
     # others. 
     return Query.from_self(self.deleted_filter(), *ent) 
    def deleted_filter(self): 
     mzero = self._mapper_zero() 
     if mzero is not None: 
      crit = mzero.class_.deleted == False 
      return self.enable_assertions(False).filter(crit) 
     else: 
      return self 

これは、ここでSQLAlchemyのドキュメント上の溶液からインスピレーションを得た:

https://bitbucket.org/zzzeek/sqlalchemy/wiki/UsageRecipes/PreFilteredQuery 

しかし、我々はまだあります上記のようにフィルタと更新を行い、このクエリクラスを使用している場合のように、更新のためにフィルタを適用するときの更新はdelete=Falseの基準を尊重しません。

db = CustomSession(with_deleted=False)() 
result = db.query(Customer).filter(Customer.id == customer_id).update({Customer.last_active_time: last_active_time }) 

は、どのように私はここに似た何かをやったSQLAlchemyの

答えて

1

で「ソフト削除」機能を実装することができます。私たちはやや違ったやり方で、すべてのデータベースアクセスが通過するサービスレイヤーを作ったのですが、コントローラのようなものでしたが、dbアクセスのためだけにResourceManagerと呼ばれ、 "Domain Driven Design" SQLAlchemyをうまく使用する上で非常に貴重です)。派生したResourceManagerは各集約ルートに対して存在します。あなたが物事を介して取得したい各リソースクラス。 (実際には単純なResourceManagerの場合もありますが、派生マネージャークラス自体は動的に生成されます)。基本クエリーを出力するメソッドがあり、その基本クエリーは渡される前にソフト削除用にフィルターされます。その後、フィルタリングのためにそのクエリに生成を追加し、最後にquery.one()またはfirst()またはall()またはcount()でそれを呼び出すことができます。注意すべきは、この種の生成的なクエリ処理のために遭遇した問題が1つあります。テーブルに何回も参加すれば自分でハングアップすることができます。フィルタリングのために、すでに結合されているテーブルを追跡しなければならない場合もありました。あなたの削除フィルタがプライマリテーブルから外れている場合は、最初のフィルタをフィルタリングするだけで、その後は無制限に参加できます。

ので、このような何か:

class ResourceManager(object): 
    # these will get filled in by the derived class 
    # you could use ABC tools if you want, we don't bother 
    model_class = None 
    serializer_class = None 

    # the resource manager gets instantiated once per request 
    # and passed the current requests SQAlchemy session 
    def __init__(self, dbsession): 
     self.dbs = dbsession 

    # hand out base query, assumes we have a boolean 'deleted' column 
    @property 
    def query(self): 
     return self.dbs(self.model_class).filter(
      getattr(self.model_class, 'deleted')==False) 

class UserManager(ResourceManager): 
    model_class = User 

# some client code might look this 
dbs = SomeSessionFactoryIHave() 
user_manager = UserManager(dbs) 
users = user_manager.query.filter_by(name_last="Duncan").first()   

は今限り、私はいつも(前述の本を参照)あまりにも他の利点を持っているのResourceManager、経由することから始めて、私は私のクエリは事前にフィルタ処理である知っています。これはソフト削除と非常に広範で厄介なdbスキーマを持つ現在のプロジェクトではうまくいきました。

hth!

0

私は

def customer_query(): 
    return db.session.query(Customer).filter(Customer.deleted == False) 

機能を作成することになり、私は使用して、ユーザー権限に基づいてフラグを設定するには、デフォルトのフラグを忘れないようにフィルタをクエリー機能を使用し、これらのものは文句を言わないコピー&ペーストし、忘れられように、などに参加します様々な場所で。

+0

これは、削除せずにクエリを停止しません。これは重大です。全体のポイントは間違いを避けるためです。 – dusual

+0

'customer_query()。filter(something).update(something2)'は、 'something'フィルタと共に削除フィルタを適用する必要があります。 – aitchnyu

関連する問題