如何使用注释在Djangorm中创建通知日期字段
我想使用djangorm中的注释创建一个名为如何使用注释在Djangorm中创建通知日期字段,django,python-2.7,django-queryset,django-orm,django-filter,Django,Python 2.7,Django Queryset,Django Orm,Django Filter,我想使用djangorm中的注释创建一个名为notification\u date的新字段 这是我的模型: SpeciesType(models.Model): # ... some species type setting fields. heat_lapse = IntegerField() estrous = IntegerField() Breeding(models.Model): # .. some breeding fields species_
notification\u date
的新字段
这是我的模型:
SpeciesType(models.Model):
# ... some species type setting fields.
heat_lapse = IntegerField()
estrous = IntegerField()
Breeding(models.Model):
# .. some breeding fields
species_type = ForeignKey(SpeciesType, related_name="breedings", on_delete=CASCADE)
created_at = DateTimeField(auto_add_now=True)
现在育种日期通知的公式是
Breeding.created_at + (SpeciesType.heat_lapse * SpeciesType.estrous) in days
例如2017年1月29日11:21PM+(3*21)天=2017年4月2日作为通知日期
from datetime import date, timedelta
from django.db import models
from django.utils.functional import cached_property
Breeding(models.Model):
# .. your breeding fields
@cached_propery
def notification_date(self):
delta = timedelta(days=(self.species_type.heat_leapse * self.species_type.estrous))
return self.created_at + delta
因此,为了实现这一点,我使用timedelta、F()对象和ExpressionWrapper创建了这个查询过滤器:
from django.db.models import F, ExpressionWrapper, DateField
from datetime import date, timedelta
Breeding.objects.annotate(
notification_date = ExpressionWrapper(
F('created_at') +
timedelta(days=(
F('species_type__heat_lapse') * F('species_type__estrous')
))
, output_field=DateField())
).filter(
notification_date__lte == date.today()
)
但是这不起作用,因为你不能在时间差内做F()。有人知道如何表达这个期望的查询吗?这对我来说是一个很大的帮助。 < P>也许考虑在你的模型上使用A。这将更容易,并且不涉及任何额外的查询。如果您小心,所有使用的值都已正确预取。您也可以像使用普通属性一样使用它,这意味着使用my\u breading\u实例访问它。notification\u date
from datetime import date, timedelta
from django.db import models
from django.utils.functional import cached_property
Breeding(models.Model):
# .. your breeding fields
@cached_propery
def notification_date(self):
delta = timedelta(days=(self.species_type.heat_leapse * self.species_type.estrous))
return self.created_at + delta
此外,该值将在首次访问后缓存
更新:
如果您确实需要对其进行注释,因为您想在通知\u日期
上进一步过滤查询集,那么您必须编写自己的聚合函数
正如您已经注意到的,您不能在注释中使用timedelta,因为要注释的值必须完全在数据库中计算。因此,只能使用数据库函数进行计算
Django提供了一些类似于SUM
、COALESCE
或similar的工具,可以在查询中生成有效的sql
然而,您需要的一个并没有在django中实现。但是你可以自己写。mysql需要的一个名为。该函数必须创建如下所示的sql:
SELECT OrderId,DATE_ADD(OrderDate,INTERVAL 30 DAY) AS OrderPayDate FROM Orders
class DateAdd(models.Func):
"""
Custom Func expression to add date and int fields as day addition
"""
function = 'DATE_ADD'
arg_joiner = ", INTERVAL "
template = "%(function)s(%(expressions)s DAY)"
output_field = models.DateTimeField()
DATE_ADD("created_at", INTERVAL ("heat_lapse" * "estrous") DAY) AS "notifiaction_date"
qs = Breeding.objects.annotate(
notifiaction_date=DateAdd('created_at', (models.F('species_type__heat_lapse') * models.F('species_type__estrous')))
)
应该是这样的:
SELECT OrderId,DATE_ADD(OrderDate,INTERVAL 30 DAY) AS OrderPayDate FROM Orders
class DateAdd(models.Func):
"""
Custom Func expression to add date and int fields as day addition
"""
function = 'DATE_ADD'
arg_joiner = ", INTERVAL "
template = "%(function)s(%(expressions)s DAY)"
output_field = models.DateTimeField()
DATE_ADD("created_at", INTERVAL ("heat_lapse" * "estrous") DAY) AS "notifiaction_date"
qs = Breeding.objects.annotate(
notifiaction_date=DateAdd('created_at', (models.F('species_type__heat_lapse') * models.F('species_type__estrous')))
)
这将创建如下所示的sql:
SELECT OrderId,DATE_ADD(OrderDate,INTERVAL 30 DAY) AS OrderPayDate FROM Orders
class DateAdd(models.Func):
"""
Custom Func expression to add date and int fields as day addition
"""
function = 'DATE_ADD'
arg_joiner = ", INTERVAL "
template = "%(function)s(%(expressions)s DAY)"
output_field = models.DateTimeField()
DATE_ADD("created_at", INTERVAL ("heat_lapse" * "estrous") DAY) AS "notifiaction_date"
qs = Breeding.objects.annotate(
notifiaction_date=DateAdd('created_at', (models.F('species_type__heat_lapse') * models.F('species_type__estrous')))
)
使用arg_joiner连接DateAdd
函数的两个参数,从而创建必要的sql,这是一个肮脏的把戏
您可以这样使用它:
SELECT OrderId,DATE_ADD(OrderDate,INTERVAL 30 DAY) AS OrderPayDate FROM Orders
class DateAdd(models.Func):
"""
Custom Func expression to add date and int fields as day addition
"""
function = 'DATE_ADD'
arg_joiner = ", INTERVAL "
template = "%(function)s(%(expressions)s DAY)"
output_field = models.DateTimeField()
DATE_ADD("created_at", INTERVAL ("heat_lapse" * "estrous") DAY) AS "notifiaction_date"
qs = Breeding.objects.annotate(
notifiaction_date=DateAdd('created_at', (models.F('species_type__heat_lapse') * models.F('species_type__estrous')))
)
我从中获取了一些,但这是一个只适用于postgres的函数。我测试了这个,它在postgres数据库上运行。我没有为mysql测试我的代码,所以你可能需要对它进行一些调整。但这基本上就是如何做到的
如果您想了解更多关于如何编写自己的表达式,或者深入了解Django源代码,并查看已经实现的表达式,比如./P>由一个实例访问它将影响它的性能,考虑有多少记录与<代码> MyAsPultIGIN实例。如果使用get()获取实例,则会找到if,但是filter可能意味着更大的记录,因此我想您必须编写自己的
Func
。这取决于您使用的数据库。对于postgres,你必须使用INTERVAL
函数,对于mysql,你必须使用DATEADD
。我使用的是mysql,Func是什么意思?类似于存储过程的东西?