Django ORM实现查询取整/保留小数/自定义函数,Fun类(一般用于聚合函数)
1. 需求分析
最近做的项目中,想到对查询结果集用一个取整,比如下面的数据
表Data
id | value |
---|---|
9998 | 185.54871 |
9999 | 941.12546 |
因为Django本身并没有带取整函数,所以一般情况下我们都这样实现取整
result = int(Data.objects.get(id=9998).value)
当然,也会这样保留小数
result_round = round(Data.objects.get(id=9998).value,2)
但是如果查询很多结果,或者说通过分组查询,返回的是一个dict,这样取整就还要自己写一次for循环,而且不如让他直接在SQL查询的时候就取整,而且SQL本身也是有取整和保留小数的函数的。
下面就是,用SQL实现保留两位小数
select Round(value,2) from DATA
比如下面这种,用的.values(),返回的是一个dict
queryset_dict = queryset.values(
name=F('city__label')).annotate(
value=Avg((F('min_salary') + F('max_salary')) / 2)
)
-> [{"name":"xxxx","value":xxxx.xxx}]
# 所以,再写个for循环取整太麻烦
2. 需求实现
经常用法1 - 继承内置Func类
Func类,是Django ORM提供的用于自定义函数的类,我们可以让函数继承自Func,自定义查询方法
下面就来定义一个Round函数
from django.db import models
class Floor(models.Func):
"""
Django ORM自定义Floor方法,用于取整,就是数据库中的Floor函数
"""
function = 'FLOOR'
# 定义用SQL中的哪个函数
template = '%(function)s(%(expressions)s)'
# 定义SQL语句模板
# %(function)s -> 定义的function
# %(expressions)s -> 实例化时传入的数据
output_field = models.IntegerField()
# 字段类型,注意要带括号,必须是实例化对象
# -------实现ROUND保留两位小数----------
class Round(models.Func):
"""
Django ORM自定义Floor方法,用于取整,就是数据库中的Floor函数
"""
function = 'ROUND'
# 定义用SQL中的哪个函数
template = '%(function)s(%(expressions)s,2)'
# 定义SQL语句模板
# %(function)s -> 定义的function
# %(expressions)s -> 实例化时传入的数据
output_field = models.FloatField()
# 字段类型,注意要带括号,必须是实例化对象
实际使用
queryset_dict = queryset.values(
name=F('city__label')).annotate(
value=Floor(Avg((F('min_salary') + F('max_salary')) / 2))
# 在这里,直接在聚合Avg后,再调用Floor,就可以取整
)
临时用法2 - 实例化Func传入
上面的用法,总是需要我们定义一个类,但是我们有时候只是想简单用一次,可以直接实例化传入
queryset_dict = queryset.values(
name=F('city__label')).annotate(
value=models.Func(
Avg((F('min_salary') + F('max_salary')) / 2),
function='Floor'
)
# template默认就是%(function)s(%(expressions)s)所以这里不传入
)
### 如果用round并且指定保留两位则需要传入template
models.Func(
数据,
function='Round',
template='%(function)s(%(expressions)s,2)'
)