peewee:如何向任何 select 添加 where 子句?

peewee: How to add where clause to any select?

我有这样的模型:

class AbstractBaseModel(peewee.Model):
    uuid = peewee.UUIDField(default=uuid4)
    is_active = peewee.BooleanField(default=False)
    is_deleted = peewee.BooleanField(default=False)
    created_at = peewee.DateTimeField(default=datetime.now)
    updated_at = peewee.DateTimeField(default=datetime.now)
    deleted_at = peewee.DateTimeField(null=True)

    class Meta:
        abstract = True
        indexes = (
            (('is_active', 'is_deleted'), False),
        )

我愿意:

  1. (默认)能够 select 行 is_active == Trueis_deleted == False

  1. select 所有行,但只是跳过 is_activeis_deleted 标志

这里有一个不是 peewee 特有的答案。

假设你上面的(未命名的)table 是 "thing",那么基本上你的模型擅长像 select * from thing.

这样的查询

现在定义一个相关视图来挑选出某些东西,挑选出你最喜欢的行子集:

sqlite> create view thing_v  as
   ...>   select uuid, created_at, updated_at, deleted_at
   ...>   from thing
   ...>   where is_active == True and is_deleted == False;

将您的模型指向 那个 关系,它恰好是一个视图(命名查询)而不是 table。

它只是 Python...向您的模型添加一个辅助方法 class。例如,如果我的博客文章有 Post class,我可能会写:

class Post(Model):
    status = IntegerField()  # 1=live, 2=draft, 3=deleted, e.g.

    @classmethod
    def visible(cls):
        return cls.select().where(cls.status == 1)

然后,我可能会有所有已发布帖子的列表视图:

@app.route('/')
def post_index():
    query = Post.visible().order_by(Post.timestamp.desc())
    return render_template('post_index.html', query=query)

我想扩展 答案。

对于连接和更复杂的查询,我们可以使用 PeeWee CTE 功能,如下所示:

class BaseModel(peewee.Model):
    is_deleted = peewee.BooleanField(default=False, index=True)

    @classmethod
    def visible_where_clause(cls):
        return cls.is_deleted == False

    @classmethod
    def visible(cls):
        return cls.select().where(cls.visible_where_clause())

    @classmethod
    def cte(cls, name=None):
        if name is None:
            name = 'CTE_{}'.format(cls.__name__)
        return cls.visible().cte(name)

    class Meta:
        abstract = True

class User(BaseModel):
    username = peewee.CharField(max_length=255, unique=True)
    is_active = peewee.BooleanField(default=True, index=True)

    @classmethod
    def visible_where_clause(cls):
        return (
            (super().visible_where_clause()) & (cls.is_active == True)
        )

UserCTE = User.cte()


class Post(BaseModel):
    status = peewee.IntegerField()  # 1=live, 2=draft, 3=deleted, e.g.
    user = peewee.ForeignKeyField(User)
    title = peewee.CharField(max_length=255)

    @classmethod
    def visible_where_clause(cls):
        return (
                (super().visible_where_clause()) & (cls.status == 1)
        )

PostCTE = Post.cte()

然后当我们只想要活跃用户的活跃帖子时,我们可以这样做:

posts = (
    Post
    .visible()
    .select(Post.title)
    .join(UserCTE, on=Post.user == UserCTE.c.id)
    .with_cte(UserCTE)
)
for post in posts:
    print(post.title)

记住

您必须通过添加 .c.<column_name> 来引用 CTE 对象,并通过添加 .with_cte(<list of all used CTEs>)

在查询末尾添加此 cte