对开发人员来说,Django的ORM 确实非常实用,但是将数据库的访问抽象出来本身是有成本的,那些愿意在数据库中探索的开发人员,经常会发现修改 ORM 的默认行为可以带来性能的提升。在本文中,我将分享在 Django 中使用数据库的 9 个技巧。
1. 过滤器聚合(Aggregation with Filter)
在 Django 2.0 之前,如果我们想要得到诸如用户总数和活跃用户总数之类的东西,我们不得不求助于条件表达式:
from django.contrib.auth.models import User
from django.db.models import (
Count,
Sum,
Case,
When,
Value,
IntegerField,
)
User.objects.aggregate(
total_users=Count('id'),
total_active_users=Sum(Case(
When(is_active=True, then=Value(1)),
default=Value(0),
output_field=IntegerField(),
)),
)
在 Django 2.0 中,添加了聚合函数的过滤器参数,使其更容易:
from django.contrib.auth.models import User
from django.db.models import Count, F
User.objects.aggregate(
total_users=Count('id'),
total_active_users=Count('id', filter=F('is_active')),
)
很棒,又短又可口。如果你正在使用 PostgreSQL,这两个查询将如下所示:
SELECT
COUNT(id) AS total_users,
SUM(CASE WHEN is_active THEN 1 ELSE 0 END) AS total_active_users
FROM
auth_users;
SELECT
COUNT(id) AS total_users,
COUNT(id) FILTER (WHERE is_active) AS total_active_users
FROM
auth_users;
第二个查询使用了 WHERE
过滤子句。
2. 查询集的结果变为具名元组(QuerySet results as namedtuples)
我是一个 namedtuples 的粉丝,同时也是 Django 2.0 的 ORM 的粉丝。
在 Django 2.0 中,values_list
方法的参数中添加了一个叫做 named
的属性。将 named
设置为 True
会将 QuerySet 作为 namedtuples 列表返回:
> user.objects.values_list(
'first_name',
'last_name',
)[0]
(‘Haki’, ‘Benita’)
> user_names = User.objects.values_list(
'first_name',
'last_name',
named=True,
)
> user_names[0]
Row(first_name='Haki', last_name='Benita')
> user_names[0].first_name
'Haki'
> user_names[0].last_name
'Benita'
3. 自定义函数(Custom functions)
Django 2.0 的 ORM 功能非常强大,而且特性丰富,但还是不能与所有数据库的特性同步。不过幸运的是,ORM让我们用自定义函数来扩展它。
假设我们有一个记录报告的持续时间字段,我们希望找到所有报告的平均持续时间:
from django.db.models import Avg
Report.objects.aggregate(avg_duration=Avg(‘duration’))
> {'avg_duration': datetime.timedelta(0, 0, 55432)}
那很棒,但是如果只有均值,信息量有点少。我们再算出标准偏差吧:
from django.db.models import Avg, Std