Django多门店场景下为所有视图添加业务上下文的优雅实现方案
Django多门店视图改造的优雅实现方案
方案选择分析
- URL参数方案:直观可分享,但每个视图需单独处理参数,用户切换门店需手动操作
- Session存储方案:一次选择全局生效,无需重复操作,但状态绑定会话,无法通过链接分享,清缓存会丢失筛选状态
- 混合方案(推荐):优先读取URL参数(支持链接分享),无参数时取Session保存的位置(提升体验),两者都无则用默认门店,兼顾两种方案的优点
具体实现步骤
1. 模型层改造
先定义Location模型,并给Employee、Item添加外键关联:
from django.db import models class Location(models.Model): name = models.CharField(max_length=100, unique=True) address = models.TextField(blank=True) def __str__(self): return self.name class Employee(models.Model): name = models.CharField(max_length=100) position = models.CharField(max_length=50) location = models.ForeignKey(Location, on_delete=models.CASCADE, related_name='employees') class Item(models.Model): name = models.CharField(max_length=100) price = models.DecimalField(max_digits=10, decimal_places=2) location = models.ForeignKey(Location, on_delete=models.CASCADE, related_name='items')
2. 通用位置筛选逻辑
写一个工具函数统一处理位置获取逻辑,避免重复代码:
def get_selected_location(request): # 优先读取URL参数中的location_id location_id = request.GET.get('location_id') if location_id: try: location = Location.objects.get(id=location_id) # 更新Session,保存用户最近选择的门店 request.session['selected_location_id'] = location_id return location except Location.DoesNotExist: pass # URL无参数时,读取Session中保存的门店 session_loc_id = request.session.get('selected_location_id') if session_loc_id: try: return Location.objects.get(id=session_loc_id) except Location.DoesNotExist: # 清除无效的Session记录 del request.session['selected_location_id'] # 都无匹配时,返回第一个门店(可根据需求改为提示用户选择) return Location.objects.first()
3. 改造视图函数
基于现有函数视图,集成位置筛选逻辑:
# 员工列表视图 def EmployeeList(request): selected_loc = get_selected_location(request) # 筛选当前门店的员工 employee_list = Employee.objects.filter(location=selected_loc) # 传递所有门店选项给模板 all_locations = Location.objects.all() return render(request, 'Employee/list.html', { 'objList': employee_list, 'selected_location': selected_loc, 'locations': all_locations }) # 商品列表视图(同理改造) def ItemList(request): selected_loc = get_selected_location(request) item_list = Item.objects.filter(location=selected_loc) all_locations = Location.objects.all() return render(request, 'Item/list.html', { 'objList': item_list, 'selected_location': selected_loc, 'locations': all_locations })
4. 模板添加筛选器
在列表模板顶部添加下拉筛选器,自动提交切换门店:
<!-- 员工列表模板 Employee/list.html --> <form method="get" action="{% url 'EmployeeList' %}"> <select name="location_id" onchange="this.form.submit()"> {% for loc in locations %} <option value="{{ loc.id }}" {% if loc.id == selected_location.id %}selected{% endif %}> {{ loc.name }} </option> {% endfor %} </select> </form> <!-- 员工列表展示 --> <div class="employee-list"> {% for emp in objList %} <div class="employee-item"> {{ emp.name }} | {{ emp.position }} | {{ emp.location.name }} </div> {% empty %} <p>当前门店暂无员工</p> {% endfor %} </div>
5. 优化:用装饰器减少重复代码
如果有多个需要筛选的视图,可以写装饰器统一注入筛选逻辑:
from functools import wraps def location_filtered(model): def decorator(view_func): @wraps(view_func) def wrapper(request, *args, **kwargs): selected_loc = get_selected_location(request) queryset = model.objects.filter(location=selected_loc) all_locations = Location.objects.all() # 将筛选结果和门店信息传入视图 kwargs.update({ 'filtered_queryset': queryset, 'selected_location': selected_loc, 'all_locations': all_locations }) return view_func(request, *args, **kwargs) return wrapper return decorator # 使用装饰器简化视图 @location_filtered(Employee) def EmployeeList(request, filtered_queryset, selected_location, all_locations): return render(request, 'Employee/list.html', { 'objList': filtered_queryset, 'selected_location': selected_location, 'locations': all_locations })
方案优势
- 用户体验友好:用户选择门店后,后续访问其他视图自动沿用该选择,无需重复操作
- 支持链接分享:带
location_id参数的URL可直接分享,对方打开后直接看到对应门店的数据 - 代码复用性高:通用工具函数和装饰器可快速扩展到其他需要筛选的视图
- 容错性强:自动处理无效的Session记录,避免报错
内容的提问来源于stack exchange,提问作者andrew
相关产品推荐
相关产品推荐

