我有一个带有Django Rest Framework的Django应用程序,该应用程序将记录存储在Postgres中。Vehicle
模型具有代表财务协议的最终支付日期的end_date
DateField
和代表支付金额的monthly_payment
FloatField
。财务付款每月一次,与最后一次付款在同一天(例如,如果end_date
为25/01/2020,则从现在到2020年1月25日,包括当月的每个月25日进行付款) 。)
我有一个ListVehicle
ListCreateAPIView
,它返回车辆记录的分页列表。我正在使用自定义PageNumberPagination
类返回data
对象以及results
数组,该对象是通过汇总Vehicle
模型中的某些字段而填充的。我想在此data
对象中包含一个字段,该字段包含要对数据库中所有Vehicle
实体支付的剩余总金额。
我尝试使用模型中的@property
字段来计算每个Vehicle
的总剩余金额,但是您can't aggregate over calculated properties(至少没有使用queryset.aggregate
),因此以下解决方案无效:
@property
def remaining_balance(self):
return max(self.remaining_monthly_payments * self.monthly_payment, 0)
@property
def remaining_monthly_payments(self):
now = datetime.datetime.now()
end = self.end_date
months = (end.year - now.year) * 12
months -= now.month + 1
months += end.month
today = now.day
final_day = end.day
if today < final_day:
months += 1
return months
[我还尝试过使用分页类中的ExpressionWrapper
,首先用Vehicle
字段注释每个time_remaining
,然后通过从remaining_balance
中提取月份并将其乘以该注释来计算time_remaining
字段由monthly_payment
。然后,remaining_balance
被聚合。
class VehicleFinancePagination(pagination.PageNumberPagination):
def paginate_queryset(self, queryset, request, view=None):
duration = ExpressionWrapper(F('end_date') - Now(), output_field=fields.DurationField())
queryset = queryset.annotate(duration=duration)
queryset = queryset.annotate(remaining_balance=ExpressionWrapper(ExtractMonth('duration') * F('monthly_payment'), output_field=FloatField()))
self.total_remaining_balance = queryset.aggregate(total_remaining_balance=Sum('remaining_balance'))[
"total_remaining_balance"]
return super(VehicleFinancePagination, self).paginate_queryset(queryset, request, view)
def get_paginated_response(self, data):
paginated_response = super(VehicleFinancePagination, self).get_paginated_response(data)
paginated_response.data['total_remaining_balance'] = self.total_remaining_balance
return paginated_response
我已经尝试过这种样式的注释的几种组合。 (包括在一个注释中进行整个计算)。每次返回0。如果我使用ExtractDay
而不是ExtractMonth
,我会得到一个值,所以我认为这里的问题是ExtractMonth
从DateField
获取一年中的月份,而不是DurationField
中完整月份的数量]就像我希望的那样,尽管this answer会建议否则
另一项无效的方法是将Vehicle
中的剩余余额/剩余月份保存起来。每月付款日期一过,Vehicle
就会过期,直到下一次保存为止,因此,合计总数将不正确。
我的方法有误吗?是否可以实现我要使用自定义数据库查询执行的操作,如果可以,那么这是最佳方法吗?
使用PostgreSQL 10.10,Django 2.2,DRF 3.9.4和Python 3.6
以下内容应为您提供所需的注释
PostgreSQL age函数将为您提供一个间隔,从该间隔我们可以获取月份数。我们可以定义一个定制的数据库函数:
class Age(Func):
function = 'AGE'
然后,我们可以链接一些注释以计算月份数。在此示例中,间隔将类似于1 year 2 mon 3 days 00:00:00
,ExtractMonth
将返回2
,因此我们还需要提取年份并将其乘以12:
queryset.annotate(
diff=Age(F('end_date'), datetime.date.today())
).annotate(
months=ExtractYear('diff') * 12 + ExtractMonth('diff')
)
然后,我们可以链接进一步的注释,在其中我们可以使用先前的注释计算剩余余额。我们必须使用ExpressionWrapper
并将其强制转换为浮点数,因为months
和monthly_payment
是不同的类型:
queryset.annotate(
diff=Age(F('end_date'), datetime.date.today())
).annotate(
months=ExtractYear('diff') * 12 + ExtractMonth('diff')
).annotate(
remaining_balance=ExpressionWrapper(F('months') * F('monthly_payment'), output_field=models.FloatField())
)