Django 1.9:为 QuerySet 创建复杂的自定义过滤器方法

Django 1.9: Create complex custom filter method for QuerySet

目标是创建一个可以链接到标准 Django 过滤器方法的 custom_filter 方法。 custom_filter 方法可能需要一些原始 SQL 代码。在最好的情况下,QuerySet 仍将被延迟计算。

最后,这样的命令就好了:

apple_query_set = Apple.objects.filter(<any standard filtering>).custom_filter()

这是型号:

class Apple(models.model):
    a = models.IntegerField()
    b = models.IntegerField()
    date = models.DateField()

custom_filter 的目标是按 (a,b) 对 Apple 实例进行分组,并且对于每个 根据 date.

组 return 只有最新的实例

这种过滤器的原始 SQL 代码如下:

custom_filter_raw_sql = """
SELECT t1.id
FROM app_apple AS t1
INNER JOIN (SELECT a, b, max(date) AS max_date
            FROM app_apple
            GROUP BY a, b) AS t2
ON t1.a = t2.a AND t1.b = t2.b AND t1.date = t2.max_date;
"""

到目前为止,为了添加custom_filter功能, 我尝试(未成功)将 objects = AppleQuerySet.as_manager() 添加到 Apple class,其中:

 class AppleQuerySet(models.QuerySet):
    def custom_filter(self):
        subquery = """
        SELECT t1.id
        FROM app_apple AS t1
        INNER JOIN (SELECT a, b, max(date) AS max_date
                    FROM app_apple
                    GROUP BY a, b) AS t2
        """
        condition = "t1.a = t2.a AND t1.b = t2.b AND t1.date = t2.max_date"
        return self.extra(tables=[subquery], where=[condition])

但是,我不确定这种方法是否可行,因为自定义查询 不仅应该适用于所有 Apple 实例 (Apple.objects.),而且应该可以将它链接到过滤的查询集 (Apple.objects.filter())

创建此自定义可链接(惰性)custom_filter 功能的最佳方法是什么?我哪里错了?非常感谢!

我认为您需要的是自定义管理器。在 Django documentation

上查看

在这里你可以看到一个例子,使用原始 SQL 代码:

from django.db import models

class PollManager(models.Manager):
    def with_counts(self):
        from django.db import connection
        cursor = connection.cursor()
        cursor.execute("""
            SELECT p.id, p.question, p.poll_date, COUNT(*)
            FROM polls_opinionpoll p, polls_response r
            WHERE p.id = r.poll_id
            GROUP BY p.id, p.question, p.poll_date
            ORDER BY p.poll_date DESC""")
        result_list = []
        for row in cursor.fetchall():
            p = self.model(id=row[0], question=row[1], poll_date=row[2])
            p.num_responses = row[3]
            result_list.append(p)
        return result_list

这是另一种方法,但我想知道您是否只能使用 order_by and distinct 的组合来达到预期的效果:

Apple.objects.order_by('a', 'b', '-date').distinct('a', 'b')

如果您在 order_bydistinct.

中保持字段顺序相同,则此组合有效

而且,如果需要,您还可以通过这种方式预先使用链式 filter

一些解释:

使用following只会把所有ab相似的对象放在一起

Apple.objects.order_by('a', 'b')

但是,您可以通过 -date(降序)

进一步对组中的对象(具有相同的 ab 值)进行排序
Apple.objects.order_by('a', 'b', '-date')

现在,所有具有相似 ab 的对象都在一起,并且在每个组中第一个元素具有最新的 date。因此,我们可以使用 distinct('a', 'b')

将它们保留在顶部
Apple.objects.order_by('a', 'b', '-date').distinct('a', 'b')