【问题标题】:Flask-SQLAlchemy: Automatic filter for 'deleted' entriesFlask-SQLAlchemy:“已删除”条目的自动过滤器
【发布时间】:2015-10-22 04:23:05
【问题描述】:

我正在开发一个庞大的代码库,该代码库执行所有数据库交互,如下所示:

ExampleClass.query.all()

其中ExampleClass继承了db.Model的属性,我们的BaseEntity定义如下:

class BaseEntity(object):
    deleted = db.Column(db.DateTime, default=None, nullable=True)
    ... # more columns

现在,当我查询 ExampleClass 的所有条目时,ExampleClass.query.all(),我希望将已删除字段设置为日期的条目,即从系统中删除的条目被排除在外。如果我不必用简单的.filter(deleted != None) 更新整个代码库,那将是理想的选择。我的解决方案是添加一个 SQLAlchemy 事件过滤器:before_compile。文档准确地告诉了我正在搜索的内容:

@event.listens_for(Query, "before_compile", retval=True)
def no_deleted(query):
    for desc in query.column_descriptions:
        if desc['type'] is ExampleClass:
            entity = desc['expr']
            query = query.filter(entity.deleted == False)
    return query

但是我无法让它工作,我遇到的最后一个错误如下:

AttributeError: 'Mapper' object has no attribute 'deleted'

我使用的库版本有:Flask==0.10.1、Flask-SQLAlchemy==1.0、SQLAlchemy==1.0.6

【问题讨论】:

    标签: python-3.x flask sqlalchemy flask-sqlalchemy


    【解决方案1】:

    我知道这是个老问题,但最近我通过这个函数让它工作:

    @event.listens_for(Query, "before_compile", retval=True) 
    def no_deleted(query):
        for desc in query.column_descriptions:
            if desc['type'] is ExampleClass:
               entity = desc['entity']
    
               limit, offset = query._limit, query._offset
               query._limit, query._offset = None, None
    
               query = query.filter(entity.deleted == False)
    
               query._limit, query._offset = limit, offset
    
        return query
    

    您需要对实体进行过滤,而不是类型。并临时移除限制和偏移量。

    Flask==1.0.2,Flask-SQLAlchemy==2.3.2,SQLAlchemy==1.2.12

    link to sqlalchemy docs for this event

    升级版:

    如果您希望能够不排除已删除的项目,可以使用此选项:

    创建 QueryMixin:

    class ShowDeleted:
        def show_deleted(self):
            q = self._clone()
            q.__dict__['__show_deleted'] = True
            return q
    

    编辑 no_deleted 函数

    def no_deleted(query):
        for desc in query.column_descriptions:
            show_deleted = query.__dict__.get('__show_deleted', False)
            if not show_deleted and desc['type'] is ExampleClass:
                entity = desc['entity']
    
                limit, offset = query._limit, query._offset
                query._limit, query._offset = None, None
    
                query = query.filter(entity.deleted == False)
    
                query._limit, query._offset = limit, offset
    
        return query
    

    如果您使用的是 FlaskSQLAlchemy,请以这种方式设置数据库:

    from flask_sqlalchemy import SQLAlchemy, BaseQuery
    
    db = SQLAlchemy(query_class= type('Query', (ShowDeleted, BaseQuery), {}))
    

    如果是原始 sqlalchemy,请使用这个 sn-p:

    from sqlalchemy.orm import Query
    db = scoped_session(sessionmaker(query_cls=type('Query', (ShowDeleted, Query), {})))
    

    现在您可以在代码中调用

    db.session.query(ExampleClass).show_deleted().all()
    

    【讨论】:

    • 这很好用。但是,当我想将“show_deleted() 属性”也应用于对象模型中的所有关系时该怎么办?因此,当我调用“db.session.query(Parent).show_deleted().all()”时,还要获取通过 Parent 模型类中的“db.relationship(Child)”配置的所有已删除子项。
    【解决方案2】:

    执行此操作的明显且简单的方法(经过测试并适用于我)是:

    class BaseEntity(db.Model):
        deleted = db.Column(db.DateTime, default=None, nullable=True)
    
    BaseEntity.query = BaseEntity.query.filter(BaseEntity.deleted == None)
    

    也就是说,其他开发人员可能希望 BaseEntity.query 以干净的查询对象开头,因此您可能会考虑使用与 query 不同的类属性:

    class BaseEntity(db.Model):
        deleted = db.Column(db.DateTime, default=None, nullable=True)
    
    BaseEntity.non_deleted = BaseEntity.query.filter(BaseQuery.deleted == None)
    
    # and then use it like …
    BaseEntity.non_deleted.all()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-07-26
      • 1970-01-01
      • 1970-01-01
      • 2018-07-28
      • 2021-04-04
      • 1970-01-01
      • 1970-01-01
      • 2016-08-30
      相关资源
      最近更新 更多