优化Django QuerySet词汇表生成的时间复杂度
优化Django QuerySet词汇表生成的时间复杂度
嗨,看了你的问题,你当前的代码因为两次遍历整个QuerySet,确实是O(n²)的时间复杂度,数据量越大效率越低。下面给你两个更高效的优化方案,都能把时间复杂度降到O(n),代码也更简洁:
方案一:Python层面一次遍历完成
这个方案不需要改动数据库查询逻辑,只需要单次遍历QuerySet就能完成分组,直接把时间复杂度降到O(n):
def get_toc(self): toc = {} qs = self.get_queryset() for q in qs: # 统一转成大写,避免同一个字母因大小写分成两组(比如A和a) first_char = q.title[0].upper() # 用setdefault省去判断键是否存在的冗余代码 toc.setdefault(first_char, []).append(q) # 对首字母按键排序,保证词汇表的有序性 return {key: toc[key] for key in sorted(toc.keys())}
整个过程只需要遍历一次QuerySet,相比原代码的两次循环,效率提升非常明显。
方案二:利用Django数据库查询优化分组(适合大数据量)
如果你的QuerySet数据量很大,把所有对象加载到内存会占用较多资源,这时候可以让数据库帮忙处理首字母提取和排序,进一步优化性能:
首先定义一个自定义数据库函数,用来提取标题的第一个字符:
from django.db.models import Func, CharField class FirstChar(Func): function = 'LEFT' template = "%(function)s(%(expressions)s, 1)" output_field = CharField()
然后在方法里通过注解和数据库排序来实现分组:
def get_toc(self): # 给每个对象添加首字母注解,并按首字母+标题排序 qs = self.get_queryset().annotate( first_char=FirstChar('title') ).order_by('first_char', 'title') toc = {} for item in qs: first_char = item.first_char.upper() toc.setdefault(first_char, []).append(item) return toc
这个方案的优势在于,数据库会先完成排序操作(如果title字段有索引,查询效率会更高),遍历的时候直接按顺序分组即可,同时能减少内存占用压力。
额外小提示
如果你的标题可能以数字、符号等非字母开头,可以加个判断把这类内容统一归类到#分组里,让词汇表更规整:
first_char = q.title[0].upper() if not first_char.isalpha(): first_char = '#' toc.setdefault(first_char, []).append(q)
备注:内容来源于stack exchange,提问作者Viktor




