2017-06-13 128 views
0

我们正在为使用龙卷风和sqlalchemy的应用程序创建服务。该应用程序使用django编写,并使用“软删除机制”。这意味着在底层的mysql表中没有删除。要将行标记为已删除,我们只需将属性“delete”设置为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

的“软删除”功能,我在这里做了类似的事情。我们做了一点不同,我们创建了一个所有数据库访问都经过的服务层,类似于控制器,但仅用于数据库访问,我们称之为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()   

现在只要我总是通过一个ResourceManager,它有其他好处也打算开始(见上述书),我知道我的查询是预过滤。对于我们当前的一个软删除和相当广泛且棘手的数据库模式的项目来说,这非常有效。

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