django orm 按天数过滤日期时间
django orm filter datetime by integer of days
我有一个名为 AccessDuration
的模型,其中两个重要字段是 duration
(整数)和 lab_start_date
(日期时间)。基于这两个领域,我想检查访问持续时间是否已过期。这是通过使用模型函数完成的。
class AccessDuration(models.Model):
....
duration = models.PositiveIntegerField(default=30, help_text=_('In days'))
lab_start_date = models.DateTimeField(verbose_name=('Start Date'), null=True)
@property
def expiration_date(self) -> Union[timezone.datetime, None]:
if self.lab_start_date:
return self.lab_start_date + timezone.timedelta(days=self.duration)
return None
@property
def is_expired(self) -> bool:
""" to check whether duration already expired or yet """
if self.expiration_date:
return timezone.now() > self.expiration_date
return False
但是,我需要这个过滤器才能在我的 Django 管理页面中使用。我试过这个,但似乎还是不行:
from django.db import models
from django.contrib import admin
from django.utils.translation import ugettext_lazy as _
from django.utils import timezone
from .models import AccessDuration
class AccessListFilter(admin.SimpleListFilter):
title = _('Is Expired')
parameter_name = 'is_expired'
def lookups(self, request, model_admin):
return [
('true', _('True')),
('false', _('False'))
]
def queryset(self, request, queryset):
value = self.value()
expiration_date = models.ExpressionWrapper(
models.F('lab_start_date') + (timezone.timedelta(days=1) * models.F('duration')),
output_field=models.DateTimeField()
)
if value == 'true':
return queryset.annotate(expiration_date=expiration_date)\
.filter(expiration_date__lt=timezone.now())
elif value == 'false':
return queryset.annotate(expiration_date=expiration_date)\
.filter(expiration_date__gte=timezone.now())
return queryset
class AccessAdmin(admin.ModelAdmin):
list_filter = (AccessListFilter, 'lab_start_date')
admin.site.register(AccessDuration, AccessAdmin)
我发现的错误;
经@William解决,我对manager做了一些修改;
class AccessDurationQuerySet(models.QuerySet):
def filter_expiration(self, is_expired: bool = False):
"""
To filter whether AccessDuration already expired or yet.
don't use the same `expiration_date` as expression key,
because it will clash with `AccessDuration.expiration_date` property.
Issue:
"""
kwargs = {'_expiration_date__gte': Now()}
if is_expired:
del kwargs['_expiration_date__gte']
kwargs['_expiration_date__lt'] = Now()
return self.all().annotate(
_expiration_date=models.ExpressionWrapper(
models.F('lab_start_date') + (timezone.timedelta(days=1) * models.F('duration')),
output_field=models.DateTimeField()
)
).filter(**kwargs)
class AccessDurationManager(
models.Manager.from_queryset(AccessDurationQuerySet),
DefaultManager
):
def published(self):
return super().published().filter(
status=AccessDurationStatusChoices.active
)
class AccessDuration(models.Model):
....
objects = AccessDurationManager()
然后在我们的ORM中;
AccessDuration.objects.filter_expiration(is_expired=True)
或者在我们的admin.py;
class ExpirationListFilter(admin.SimpleListFilter):
title = _('Is Expired')
parameter_name = 'is_expired'
def lookups(self, request, model_admin):
return [
(True, True),
(False, False)
]
def queryset(self, request, queryset):
value = str(self.value()).lower()
if value == 'true':
return queryset.filter_expiration(is_expired=True)
elif value == 'false':
return queryset.filter_expiration(is_expired=False)
return queryset
class AccessAdmin(admin.ModelAdmin):
...
list_filter = (ExpirationListFilter, ...)
你应该使用另一个expiration_date
的名称来注释。例如:
from django.db.models.functions import Now
if value == 'true':
return queryset.annotate(
<strong>_expiration_date</strong>=expiration_date
).filter(<strong>_expiration_date</strong>__lt=Now())
elif value == 'false':
return queryset.annotate(
<strong>_expiration_date</strong>=expiration_date
).filter(<strong>_expiration_date</strong>__gte=Now())
我有一个名为 AccessDuration
的模型,其中两个重要字段是 duration
(整数)和 lab_start_date
(日期时间)。基于这两个领域,我想检查访问持续时间是否已过期。这是通过使用模型函数完成的。
class AccessDuration(models.Model):
....
duration = models.PositiveIntegerField(default=30, help_text=_('In days'))
lab_start_date = models.DateTimeField(verbose_name=('Start Date'), null=True)
@property
def expiration_date(self) -> Union[timezone.datetime, None]:
if self.lab_start_date:
return self.lab_start_date + timezone.timedelta(days=self.duration)
return None
@property
def is_expired(self) -> bool:
""" to check whether duration already expired or yet """
if self.expiration_date:
return timezone.now() > self.expiration_date
return False
但是,我需要这个过滤器才能在我的 Django 管理页面中使用。我试过这个,但似乎还是不行:
from django.db import models
from django.contrib import admin
from django.utils.translation import ugettext_lazy as _
from django.utils import timezone
from .models import AccessDuration
class AccessListFilter(admin.SimpleListFilter):
title = _('Is Expired')
parameter_name = 'is_expired'
def lookups(self, request, model_admin):
return [
('true', _('True')),
('false', _('False'))
]
def queryset(self, request, queryset):
value = self.value()
expiration_date = models.ExpressionWrapper(
models.F('lab_start_date') + (timezone.timedelta(days=1) * models.F('duration')),
output_field=models.DateTimeField()
)
if value == 'true':
return queryset.annotate(expiration_date=expiration_date)\
.filter(expiration_date__lt=timezone.now())
elif value == 'false':
return queryset.annotate(expiration_date=expiration_date)\
.filter(expiration_date__gte=timezone.now())
return queryset
class AccessAdmin(admin.ModelAdmin):
list_filter = (AccessListFilter, 'lab_start_date')
admin.site.register(AccessDuration, AccessAdmin)
我发现的错误;
经@William解决,我对manager做了一些修改;
class AccessDurationQuerySet(models.QuerySet):
def filter_expiration(self, is_expired: bool = False):
"""
To filter whether AccessDuration already expired or yet.
don't use the same `expiration_date` as expression key,
because it will clash with `AccessDuration.expiration_date` property.
Issue:
"""
kwargs = {'_expiration_date__gte': Now()}
if is_expired:
del kwargs['_expiration_date__gte']
kwargs['_expiration_date__lt'] = Now()
return self.all().annotate(
_expiration_date=models.ExpressionWrapper(
models.F('lab_start_date') + (timezone.timedelta(days=1) * models.F('duration')),
output_field=models.DateTimeField()
)
).filter(**kwargs)
class AccessDurationManager(
models.Manager.from_queryset(AccessDurationQuerySet),
DefaultManager
):
def published(self):
return super().published().filter(
status=AccessDurationStatusChoices.active
)
class AccessDuration(models.Model):
....
objects = AccessDurationManager()
然后在我们的ORM中;
AccessDuration.objects.filter_expiration(is_expired=True)
或者在我们的admin.py;
class ExpirationListFilter(admin.SimpleListFilter):
title = _('Is Expired')
parameter_name = 'is_expired'
def lookups(self, request, model_admin):
return [
(True, True),
(False, False)
]
def queryset(self, request, queryset):
value = str(self.value()).lower()
if value == 'true':
return queryset.filter_expiration(is_expired=True)
elif value == 'false':
return queryset.filter_expiration(is_expired=False)
return queryset
class AccessAdmin(admin.ModelAdmin):
...
list_filter = (ExpirationListFilter, ...)
你应该使用另一个expiration_date
的名称来注释。例如:
from django.db.models.functions import Now
if value == 'true':
return queryset.annotate(
<strong>_expiration_date</strong>=expiration_date
).filter(<strong>_expiration_date</strong>__lt=Now())
elif value == 'false':
return queryset.annotate(
<strong>_expiration_date</strong>=expiration_date
).filter(<strong>_expiration_date</strong>__gte=Now())