Django 1.9:为 QuerySet 创建复杂的自定义过滤器方法
Posted
技术标签:
【中文标题】Django 1.9:为 QuerySet 创建复杂的自定义过滤器方法【英文标题】:Django 1.9: Create complex custom filter method for QuerySet 【发布时间】:2016-06-21 02:42:22 【问题描述】:目标是创建一个custom_filter
方法,该方法可以链接到标准的 Django 过滤器方法。 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 实例进行分组,并为每个
group 根据date
只返回最新的实例。
此类过滤器的原始 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 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
功能的最佳方法是什么?我哪里错了?非常感谢!
【问题讨论】:
是否有特定原因不能通过使用查询本身的所有过滤条件直接perform raw queries? @AKS 这可能是一个解决方案,但我不知道如何确保 SQL 代码可以任意链接到 QuerySet(而不是对整个 Apple 表进行过滤)。我很高兴看到一些代码如何做到这一点。 (另外,我不确定这个解决方案是否允许惰性评估。) 【参考方案1】:这是另一种方式,但我想知道您是否只能使用order_by
和distinct
的组合来达到预期的效果:
Apple.objects.order_by('a', 'b', '-date').distinct('a', 'b')
如果您在 order_by
和 distinct
中保持相同的字段顺序,则此组合有效。
而且,如果需要,您还可以预先使用链接的filter
。
一些解释:
使用following只会将所有具有相似a
和b
的对象放在一起
Apple.objects.order_by('a', 'b')
但是,您可以通过-date
(按降序排列)进一步对组中的对象(具有相同的a
和b
值)进行排序
Apple.objects.order_by('a', 'b', '-date')
现在,所有具有相似a
和b
的对象都在一起,并且每个组中的第一个元素具有最新的date
。因此,我们可以使用distinct('a', 'b')
将它们保持在顶部
Apple.objects.order_by('a', 'b', '-date').distinct('a', 'b')
【讨论】:
这是一个优雅的答案! @AKS,您认为(在这种情况下当然)通过自定义 SQL 添加过滤器链步骤的笨拙方法在技术上是可行的吗?以防万一,某处是否有示例代码? 谢谢@elke,你坚持你提到的方法有什么特别的原因吗?我这样说是因为使用上述现有的 django 功能可以轻松实现您想要的结果。 @AKS,对于上面的确切示例,您的方法绝对是首选。我的想法是,如果另一个用户偶然发现这篇文章,“如何将类似复杂的 sql 查询插入到查询集过滤器”的答案可能是她要搜索的,而不是我特定示例的解决方案问题。然而,也许所有的问题最终都有这么优雅的解决方案?【参考方案2】:我认为您需要的是自定义管理器。查看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
【讨论】:
如果返回值应该是 QuerySet,这个解决方案会是什么样子?该方法是否可以作为链式过滤器应用(而不是在整个 Apple 表上重新运行查询?)。此解决方案是否允许延迟评估? docs.djangoproject.com/es/1.9/topics/db/managers/… 这里展示了如何从自定义管理器创建自定义查询集,它应该回答前两个问题。我不知道“允许延迟评估”是什么意思以上是关于Django 1.9:为 QuerySet 创建复杂的自定义过滤器方法的主要内容,如果未能解决你的问题,请参考以下文章
是否可以通过检查它们的交集是否为空来过滤 django 多对多 QuerySet 字段?