Django读取MySQL视图数据

Django中的业务模型有个限制:必须有ID字段。一般情况下这没有问题,但是一旦我们需要访问数据库视图数据时,这个限制就会带来麻烦。

数据库视图一般仅为全量业务数据的局部,而且通常涉及统计结果,或者多表联查结果。在这样的场景里,业务对象ID包含到视图中就缺乏实际意义了。

但是,整个应用都在Django框架下开发,且它已经提供了相当好用的ORM模块,这时候再引入其他数据库访问框架,一来应用显得更加笨重,再者增加维护成本,得不偿失。

那有没有办法解决这个矛盾呢?

有的,那就是使用row_number()函数获取记录行号作为ID,并且将模型的数据库映射置为手工管理即可,即managed = False

如果数据库使用的是MySQL 8.0之前的版本,那就需要自定义函数来模拟它。其他数据库系统都有此窗口函数。

首先创建数据库视图:

1
2
3
4
5
6
7
8
9
10
create or replace view revenue_monthly_view as
select row_number() over () as id,
vt.*
from (select sum(`t1`.`sold_price` - `t1`.`discount`) as `quantity`,
date_format(`t1`.`settled_at`, '%Y-%m') as `month`
from `product_items` `t1`
where `t1`.`status` = 'sold'
group by `month`
) vt
order by `month` desc;

接着定义好映射到数据库视图的模型:

1
2
3
4
5
6
7
8
class RevenueMonthly(models.Model):
id = models.BigAutoField('ID', primary_key=True, max_length=16)
month = models.CharField('月份', max_length=16)
quantity = models.DecimalField('数额', max_digits=16, decimal_places=2)

class Meta:
managed = False
db_table = 'revenue_monthly_view'

最后,就可以跟通常QuerySet的使用一样了。

1
2
# 获取最近12个月的收入统计
last_12_month_revenue = RevenueMonthly.objects.filter(month__gt=one_year_before)