使用 F 和 Q 表达式进行 Django 模型过滤

Posted

技术标签:

【中文标题】使用 F 和 Q 表达式进行 Django 模型过滤【英文标题】:Django model filtering with F and Q expressions 【发布时间】:2015-09-01 09:07:40 【问题描述】:

我正在 Django 中制作调度程序,但在过滤每周日历视图的事件时遇到问题。日历支持多日活动,而我当前的过滤器不适用于此每周视图。

这是我的模型:

 class Event(models.Model):
    title = models.CharField(max_length=40)
    start = models.DateTimeField()
    end = models.DateTimeField()
    description = models.TextField()
    all_day = models.BooleanField(default=False)
    recuring = models.BooleanField(default=False)
    recuring_end = models.DateTimeField(blank=True, null=True)

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return '/cab/event/%i/' % self.id

我正在尝试过滤给定一周内发生的事件。对于单日活动,我会做类似的事情。

events = Event.objects.order_by('start').filter(Q(start__gte=monday) | Q(end__lte=sunday))

这可以检索一周内发生的所有单日事件。它也适用于在给定一周内开始或停止的多日事件。问题在于检索在一周之前开始并在一周之后完成但确实跨越一周的对象。

我的想法是尝试过滤掉任何超过 9 天的事件(即从前一周的星期日开始到下周的星期一结束),因为我知道这些事件很少见,不会完全破坏性能。我想不指定日期范围这样做,因为这不是动态的。

为了尽量减少对性能的影响,我尝试使用 F 表达式来评估事件的持续时间以及事件的开始和结束。我的第一个想法是做类似的事情:

my_events = Event.objects.order_by('start').filter(Q(start__gte=monday) | Q(end__lte=sunday) | Q( (F('end_day') - F('start_day')) >= 9 ) )

但我收到错误'bool' object is not iterable

也试过了:

my_events = Event.objects.order_by('start').filter(Q(start__gte=monday) | Q(end__lte=sunday) | Q( (F('end_day') - F('start_day')) >= datetime.timedelta(days=9) ) )

但是得到can't compare datetime.timedelta to ExpressionNode

任何人都煽动如何做这样的事情?

【问题讨论】:

Django database query: How to filter objects by date range? 的可能重复项 @sobolevn 不重复,因为我不想指定日期范围 Q 函数适用于=,不适用于>=,这就是您收到错误'bool' object is not iterable 的原因 Q(end_day__gte=start_day+9) 不起作用? @SylvainBiehler 不想要 9 天,而是 9 天或更多。 Q function 是否有替代 >= 的方法? 【参考方案1】:
from datetime import timedelta

Event.objects.filter(end__gt=F('start') + timedelta(days=9))

文档有example。

更新:

事件,跨越 9 天且(开始晚于星期一或结束早于星期日),由 start 排序。

(Event.objects
 .filter(end__gt=F('start') + timedelta(days=9),
         Q(start__gte=monday) | Q(end__lte=sunday))
 .order_by('start'))

【讨论】:

【参考方案2】:

我认为 Arrow 可能对你的探索有所帮助。

也许有足够精通的人可以为您提供准确的代码,但与此同时,此视频可能会有所帮助:

https://godjango.com/72-using-arrow-for-better-datetime/

【讨论】:

非常感谢视频,Arrow 看起来很棒,但它再次使用了我不想做的范围【参考方案3】:

只是对@https://***.com/users/4907653/f43d65的警告 的答案,最后一次查询查找 Q 对象可能是无效的。

Reference to docs

查找函数可以混合使用 Q 对象和关键字参数。提供给查找函数的所有参数(无论是关键字参数还是 Q 对象)都被“与”在一起。但是,如果提供了 Q 对象,则它必须位于任何关键字参数的定义之前。例如:

Poll.objects.get(
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
question__startswith='Who',)

... 将是一个有效的查询,相当于前面的示例;但是:

# INVALID QUERY
Poll.objects.get(
    question__startswith='Who',
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
)

... 无效。

【讨论】:

以上是关于使用 F 和 Q 表达式进行 Django 模型过滤的主要内容,如果未能解决你的问题,请参考以下文章

Django基础五之django模型层多表操作

如何使用 Django Querysets 和 Q() 与相同模型类型的对象进行比较?

Django 之 models的 F() 和 Q() 函数

Django 数据库操作

Django ORM F查询和Q查询

Django基础之模型(models)层