Flask-SQLAlchemy:自动过滤“已删除”条目

时间:2015-07-30 17:11:48

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

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

ExampleClass.query.all()

ExampleClass继承了db.ModelBaseEntity的属性,其定义如下:

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

2 个答案:

答案 0 :(得分:2)

我知道这是一个老问题,但是最近我通过此功能使其起作用:

@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,请使用以下代码段:

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

现在您可以在代码调用中

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

答案 1 :(得分:0)

这样做的明显而简单的方法(经测试并适用于我)将是:

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