实现"软删除"使用sqlalchemy的系统

时间:2017-06-13 13:05:04

标签: orm sqlalchemy

我们正在使用tornado和sqlalchemy为应用创建服务。应用程序是用django编写的,并使用"软删除机制"。这意味着底层mysql表中没有删除。要将行标记为已删除,我们只需设置属性"删除"如实。但是,在服务中我们使用sqlalchemy。最初,我们开始在sqlalchemy本身的查询中添加check for delete,如:

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 docs的解决方案:

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中的功能

2 个答案:

答案 0 :(得分:2)

我会创建一个函数

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

我使用查询函数来忘记默认标志,根据用户权限设置标志,使用连接过滤等,这样就不会在不同的地方复制粘贴和遗忘这些标记。

答案 1 :(得分:1)

我在这里做了类似的事。我们做得有点不同,我们制作了一个服务层,所有数据库访问都经过,有点像控制器,但只有数据库访问,我们称之为ResourceManager,它的灵感来自“域驱动设计”(很棒的书) ,对于使用SQLAlchemy很有价值)。每个聚合根存在一个派生的ResourceManager,即。您希望获得的每个资源类。 (虽然有时候对于非常简单的ResourceManagers,派生的管理器类本身是动态生成的)它有一个方法可以提供你的基本查询,并且在你的软删除分发之前,基本查询会被过滤掉。从那时起,您可以生成地添加到该查询以进行过滤,最后使用query.one()或first()或all()或count()调用它。注意,我遇到过这种生成查询处理的问题,如果你加入一个表太多次,你可以自己挂起来。在某些情况下,我们必须跟踪哪些表已经加入过滤。如果您的删除过滤器不在主表中,只需先过滤掉,然后您就可以加入了。

这样的事情:

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()        

现在,只要我总是通过一个资源管理器开始,这也有其他好处(参见前面提到的书),我知道我的查询是预先过滤的。对于我们来说,这对于我们当前具有软删除和相当广泛且棘手的数据库模式的项目非常有效。

HTH!