如何精简这段Django视图函数?求Python框架熟手解决方案
优化你的Django视图:精简重复代码的几种实用方式
首先,你的原代码最大的问题是重复的过滤逻辑和上下文构建——16次几乎一样的filter调用,不仅写起来麻烦,后续维护也容易出错(比如漏改某个分类的关键词)。作为常年和Django打交道的开发者,我会优先用以下几种方式精简:
1. 用字典推导式+映射列表(最推荐的基础优化)
把分类关键词和对应的上下文键做成一个映射列表,然后用字典推导式一次性生成上下文。这是最直接的重构,完全保留原逻辑,同时大幅减少代码量:
# 可以把这个映射提到模块顶部,作为全局常量,方便后续维护 MENU_CATEGORY_MAPPINGS = [ ('p', 'PANCAKE'), ('e', 'EGG'), ('pn', 'PANNIS'), ('s', 'SUBS'), ('w', 'WRAPS'), ('t', 'TEA'), ('f', 'FRAPPE'), ('sm', 'SMOOTHIE'), ('gf', 'GLUTENF'), ('wa', 'WAFFLES'), ('to', 'TOAST'), ('hp', 'HOTPASTA'), ('b', 'BAGELS'), ('fr', 'FRIES'), ('sa', 'SALADS'), ('d', 'DESSERTS'), ] def test(request): context = { key: MenuItem.objects.filter(item_category__icontains=category) for key, category in MENU_CATEGORY_MAPPINGS } return render(request, 'posts/test.html', context)
为什么这是首选?
- 易维护:以后要新增/修改分类,只需要在
MENU_CATEGORY_MAPPINGS里加一行或改一行,不用重复写filter和上下文键值对; - 逻辑一致:完全保留原代码的
icontains过滤行为,每个分类对应独立的QuerySet(延迟执行,不会立即查询数据库); - 代码简洁:去掉了大量重复代码,可读性更高。
2. 减少数据库查询次数(性能优化方向)
原代码会触发16次数据库查询(每个filter在模板中遍历的时候才会执行)。如果你的菜单数据量较大,或者想优化查询次数,可以用一次查询获取所有相关条目,再在内存中分组:
from django.db.models import Q MENU_CATEGORY_MAPPINGS = [ # 同上的映射列表 ] def test(request): # 构建OR查询,一次性获取所有匹配任一分类的菜品 category_query = Q() for _, category in MENU_CATEGORY_MAPPINGS: category_query |= Q(item_category__icontains=category) all_matching_items = MenuItem.objects.filter(category_query) # 在内存中按分类分组(注意保持icontains的大小写不敏感特性) context = {} for key, category in MENU_CATEGORY_MAPPINGS: context[key] = [ item for item in all_matching_items if category.lower() in item.item_category.lower() ] return render(request, 'posts/test.html', context)
优缺点说明
- 优点:把数据库查询从16次降到1次,减少数据库IO开销;
- 缺点:所有匹配的菜品会加载到内存中,如果数据量极大(比如上万条),内存占用会增加。适合中小型菜单场景。
3. 进阶:如果菜品分类是结构化数据(模型外键)
如果你的item_category不是字符串字段,而是关联到Category模型的外键,那可以用更优雅的方式:
# 假设Category模型有name字段 def test(request): # 先获取需要的分类对象 target_categories = Category.objects.filter( name__in=[cat for _, cat in MENU_CATEGORY_MAPPINGS] ) # 用prefetch_related一次性获取分类和对应的菜品 context = { key: category.menuitem_set.all() for key, category_name in MENU_CATEGORY_MAPPINGS for category in target_categories if category.name == category_name } return render(request, 'posts/test.html', context)
不过这需要你调整数据模型结构,适合从一开始就规划好分类的场景,能彻底避免字符串匹配的麻烦。
最后一点小建议
如果这个视图是用来渲染菜单页面,建议给MenuItem的item_category字段加索引——因为你频繁用icontains查询,索引能大幅提升查询速度。可以在模型里这样定义:
class MenuItem(models.Model): item_category = models.CharField(max_length=100, db_index=True) # 其他字段...
内容的提问来源于stack exchange,提问作者Quinnyboy
相关产品推荐
相关产品推荐

