django - 如何用创建后的天数注释查询集
问题描述
我正在尝试根据促销进行一些复杂的排序:自创建以来每 7 天促销一篇文章(文章在 30 天后过期)。
我的方法是使用自创建以来的天数来注释查询集,但days_since_creation
在我的代码中,注释字段 ( ) 的值始终为 0。
from datetime import timedelta
from django.test import TestCase
from django.db.models import ExpressionWrapper, F
from django.db.models.fields import IntegerField
from django.utils import timezone
from .models import Article
# class Article(models.Model):
# title = models.CharField(max_length=150)
# creation_date = models.DateTimeField()
#
# def __str__(self):
# return self.title
class ArticleTestCase(TestCase):
def test_days_since_creation(self):
now = timezone.now()
objects_data = [
Article(
title='Alrp',
creation_date=(now - timedelta(days=5)) # 5 days ago
),
Article(
title='Bopp',
creation_date=(now - timedelta(days=7)) # 7 days ago
),
Article(
title='Crkp',
creation_date=(now - timedelta(days=14)) # 14 days ago
),
]
Article.objects.bulk_create(objects_data)
article_set = Article.objects\
.annotate(
days_since_creation=ExpressionWrapper(
now - F('creation_date'),
output_field=IntegerField()
)
)
for article in article_set:
print(article.days_since_creation)
# all 3 objects print "0"
我预计每个对象的值分别为 5、7、14。我什至尝试过DurationField
,但只是打印了 0:00:00。
之后,如果值为in或 1,则我将再次使用order
值为 0的字段对查询集进行注释,否则为 1 。days_since_past
[0, 7, 14, 21, 28,]
order_by('order')
解决方案
这有点hacky,但有效。您需要转换now
为DateTimeField
. 结果将DurationField
符合 timedelta 的标准。因为order
我们将使用Case
来检查我们是否处于选定的持续时间,然后我们对其进行排序。
我专门将初始顺序更改Articles
为测试顺序。
from datetime import timedelta
from django.test import TestCase
from django.db.models import ExpressionWrapper, F, Value, Case, When
from django.db.models.fields import DateTimeField, DurationField, BooleanField
from django.utils import timezone
from .models import Article
class ArticleTestCase(TestCase):
def test_days_since_creation(self):
now = timezone.now()
order_in_variants = [
timedelta(days=0),
timedelta(days=7),
timedelta(days=14),
timedelta(days=21),
timedelta(days=28),
]
objects_data = [
Article(
title='Crkp',
creation_date=(now - timedelta(days=14)) # 14 days ago
),
Article(
title='Alrp',
creation_date=(now - timedelta(days=5)) # 5 days ago
),
Article(
title='Bopp',
creation_date=(now - timedelta(days=7)) # 7 days ago
),
]
Article.objects.bulk_create(objects_data)
article_set = Article.objects.all().annotate(
days_since_creation=ExpressionWrapper(
Value(now, DateTimeField()) - F('creation_date'),
output_field=DurationField()
)
).annotate(
order=Case(
When(days_since_creation__in=order_in_variants, then=Value(False)),
default=Value(True),
output_field=BooleanField(),
)
).order_by('order')
for article in article_set:
print(article.days_since_creation.days, article.order)
# 14 False
# 7 False
# 5 True
您可能还想days_since_creation
在范围内检查,而不是恰好相差 7 天。让它更丑陋,但仍然:
order_in_variants = [
(timedelta(days=0), timedelta(days=0, hours=23, minutes=59, seconds=59, microseconds=999999)),
(timedelta(days=7), timedelta(days=7, hours=23, minutes=59, seconds=59, microseconds=999999)),
(timedelta(days=14), timedelta(days=14, hours=23, minutes=59, seconds=59, microseconds=999999)),
(timedelta(days=21), timedelta(days=21, hours=23, minutes=59, seconds=59, microseconds=999999)),
(timedelta(days=28), timedelta(days=28, hours=23, minutes=59, seconds=59, microseconds=999999)),
]
# ...
order=Case(
When(days_since_creation__range=order_in_variants[0], then=Value(False)),
When(days_since_creation__range=order_in_variants[1], then=Value(False)),
When(days_since_creation__range=order_in_variants[2], then=Value(False)),
When(days_since_creation__range=order_in_variants[3], then=Value(False)),
When(days_since_creation__range=order_in_variants[4], then=Value(False)),
default=Value(True),
output_field=BooleanField(),
)
推荐阅读
- azure-devops - 重定向到 DefaultCollection 以外的集合
- android - 如何防止 StaggeredGridLayout Manager 重新排序 RecyclerView 项目?
- javascript - 如何遍历 firebase get 的结果
- python - RegEx 用于匹配两位数和除新行和点之外的所有内容
- awk - AWK:显示 2 列 2 个文件,其中第二列具有唯一数据
- c# - 从两个线程写入一个txt文件
- termios - 启动 localstack 失败 - 缺少 termios
- android - 如何在应用程序关闭时的特定时间启动应用程序
- excel - 在 MATLAB 中使用 xlswrite
- arrays - 如何制作一个函数来知道不相交的集合数组是否代表单个分区?