归档查询优化

博客一般都会提供一个归档(历程)页来查看自己的文章史

简单来说归档页面通常会对年或者月份来做分组, 从而渲染不同的对应块, 我的归档页就是用年来分组的

看看渲染的效果:


首先文章要怎么来分组呢? 我最开始的时候是手动对模型进行分组的, 看看最开始的代码:

# 获取所有文章查询集
article_query = Article.objects.order_by('create_time')
# 统计个数
article_count = article_query.count()
# 获取所有文章第一个和最后一个的年份
last_yaer = int(article_query.last().create_time.strftime("%Y"))
first_year = int(article_query.first().create_time.strftime("%Y"))
# 计算两者差值
gap = last_yaer - first_year
year_list = []
if gap != 0:
    # 如果不等于0则遍历加入
    for i in range(gap+1):
        year_list.append(first_year)
        first_year = first_year + 1
else:
    year_list.append(last_yaer)
# 遍历year_list从article_query中过滤对应年份的查询集放入字典中
article_set = {}
for i in year_list:
    article_set.setdefault(i, article_query.filter(create_time__year=i))

article_set就是我最终做好的分组, 传入模板来进行对应的渲染

好像这么直接使用貌似没什么问题, 就是不知道数据量大的情况会不会对性能有影响, 关于分组的话Django ORM里提供了更换的办法, 搭配itertools, operator来使用迭代器进行分组

修改后的代码:

ExtractYear是orm提供的对DateField进行筛选过滤的方法, 使用annotate进行分组, 当然也有month/day/week等等对应的时间属性, 详情自己参考源码

operator.attrgetter: 返回一个可从操作数中获取 attr 的可调用对象

import itertools, operator
from django.db.models.functions import ExtractYear
from django.db.models import Count
# 获取所有文章, ExtractYear按年分组, 由于在模型里设置了排序, 添加order_by来控制排序
articles = Article.objects.annotate(year=ExtractYear('create_time')).order_by('year')
# 统计个数
article_count = articles.count()
# 使用annotate获取同年份的个数进行Count操作
year_dict = articles.values('year').annotate(nums=Count('year'))
# # year_dict: <QuerySet [{'year': 2019, 'nums': 18}, {'year': 2020, 'nums': 18}]>
# 遍历出year_dict分组的年份
year_list = [i['year'] for i in year_dict[::-1]]
# 指定year属性
sort_key = operator.attrgetter('year')
# itertools.groupby对sort_key分组, 生成一个迭代器
# list(obj_iterator)把迭代器转换成list对象
group_dict = {year: list(obj_iterator) for year, obj_iterator in itertools.groupby(list(articles), sort_key)}
# 由于今年的要排在第一位, 进行一下排序, reverse默认为False
article_set = dict(sorted(group_dict.items(), key=lambda x: x[0], reverse=True))

itertools.groupby解析:

itertools.groupby对排序过后的进行sort_key分组
返回: <itertools.groupby object at 0x7fab62ed1638> 对象

itertools.groupby object则包含类似[(sort_key,articles生成一个迭代器)]这样的格式
如: [(2019, <itertools.grouper object at 0x7fa98852b860>), (2020, <itertools.grouper object at 0x7fa98852b898>), ....]

使用list可以把<itertools._grouper object>转换为列表

再使用for循环遍历itertools.groupby对象生成所需的格式
如: {2019: [<Article: 1>, <Article: 2>], 2020: [<Article: 3>, <Article: 4>]}

这样子7行就完成了之前的15行代码的操作, 由于直接进行了分组, 就省去了自己把文章对象分组的操作, 一下代码就清爽了许多.


如果本文对你有启发,或者对本文有疑问或者功能/方法建议,可以在下方做出评论,或者直接联系我,谢谢您的观看和支持!

添加新评论

本站现已启用评论投票,被点踩过多的评论将自动折叠。与本文无关评论请发留言板。请不要水评论,谢谢。

已有 0条评论