简体   繁体   English

如何仅使用 CASE 或 COALESCE 表达式过滤 Django 查询集?

[英]How can I filter a Django queryset using just a CASE or a COALESCE expression?

I'm basing a Django queryset on a functioning postgres query that has the following WHERE condition:我将 Django 查询集基于具有以下 WHERE 条件的功能性 postgres 查询:

... WHERE COALESCE("project_story"."owner_id" = [INSERT_USER_ID], "project_story"."published_date" IS NOT NULL)

In english: if the user is the owner of the story, then include the story.英文:如果用户是故事的所有者,则包含故事。 If the user is not the owner and if the story is unpublished, then exclude it.如果用户不是所有者并且故事未发布,则将其排除。

Ideally, the Django ORM should allow something like:理想情况下,Django ORM 应该允许以下内容:

    queryset = queryset.filter(
        Coalesce(
            Q(owner_id=user.id),
            Q(published_date__isnull=False)
        )
    )

But upon executing, Django throws the error:但是在执行时,Django 会抛出错误:

TypeError: 'Coalesce' object is not iterable TypeError:“Coalesce”对象不可迭代

Unfortunately, I need this conditional filtering at the database level.不幸的是,我需要在数据库级别进行条件过滤。

Is there a notation or approach that allows selection using a Coalesce expression?是否有允许使用 Coalesce 表达式进行选择的符号或方法?

I'd prefer not to use rawsql or queryset.extra.我不想使用 rawsql 或 queryset.extra。

I figured this out by myself.这是我自己想出来的。 Since no real answer has been posted yet, here is my solution:由于尚未发布真正的答案,这是我的解决方案:

        return queryset.all().annotate(
            viewable=Case(
                When(owner_id=user.id, then=True),
                When(published_date__isnull=False, then=True),
                default=False,
                output_field=db.models.BooleanField()
            ),
        ).filter(
            viewable=True
        )

Fairly unreadable, isn't it?相当难以阅读,不是吗? The resulting SQL is just as ugly:生成的 SQL 也同样丑陋:

AND CASE WHEN ("project_story"."owner_id" = [INSERT USER ID]) THEN True WHEN ("project_story"."published_date" IS NOT NULL) THEN True ELSE False END = True) ORDER BY "project_story"."image_count" DESC

Although using CASE leads to the same result as the original query, I'd still like less-verbose code.尽管使用 CASE 会导致与原始查询相同的结果,但我仍然希望使用不那么冗长的代码。

Until then, I'll mark my question as answered.在那之前,我会将我的问题标记为已回答。

Tiny beginning of code fragment for writing your own Expression class for COALESCE(), see official documentation link below for full source.用于为 COALESCE() 编写自己的 Expression 类的代码片段的微小开头,请参阅下面的官方文档链接以获取完整源代码。

Note: not all databases have a COALESCE(), which I assume is why this isn't an official Expression already.注意:并非所有数据库都有 COALESCE(),我认为这就是为什么这不是官方表达式的原因。 It's probably possible to write some code which turns this into a standard Case(...) expression as a fallback, then use COALESCE() for the db specific sql conversions.可能可以编写一些代码将其转换为标准 Case(...) 表达式作为后备,然后使用 COALESCE() 进行特定于 db 的 sql 转换。 I leave that as an exercise for the reader ;-)我把它作为练习留给读者;-)

from django.db.models import Expression

class Coalesce(Expression):
    template = 'COALESCE( %(expressions)s )'

    ...

Official documentation for the full code: Django Models - Writing your own Query Expressions完整代码的官方文档: Django Models - Writing your own Query Expressions

I believe the result of the Q() is always True or False, so using it in Coalesce expression does not work.我相信 Q() 的结果总是 True 或 False,所以在 Coalesce 表达式中使用它是行不通的。 The solution is to use conditional expression, combining Case and filter.解决方案是使用条件表达式,结合 Case 和 filter。 You could find all information you need here in the documentation.您可以在文档中找到所需所有信息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM