Django视图编写最佳实践及代码重构咨询:请求传递与消息处理
原代码
service.py
def add_goods_to_cart(goods_id, user, addend): goods = Goods.objects.filter(pk=goods_id).first() if goods: the_goods_already_in_cart = Cart.objects.filter(user=user, goods=goods, order=None).first() if the_goods_already_in_cart: the_goods_already_in_cart.quantity = (the_goods_already_in_cart.quantity + addend) if the_goods_already_in_cart.quantity == 0: the_goods_already_in_cart.delete() else: the_goods_already_in_cart.save() else: Cart.objects.create(user=user, goods=goods, quantity=1) status = 200 else: status = 400 return status
views.py
class AddToCart(LoginRequiredMixin, View): def post(self, request): goods_id = request.POST.get('goods_id') addend = int(request.POST.get('addend')) assert (addend == 1 or addend == -1) status = add_goods_to_cart(goods_id, request.user, addend) if status == 200: act = "added to cart" if addend > 0 else "removed from cart" messages.add_message(request, messages.INFO, 'Goods "{}" {}.'.format(goods.name, act)) return redirect(request.META['HTTP_REFERER']) else: return HttpResponse("Wrong goods id", status=status)
用户疑问
- 能否将request从views.py传递到service.py?我认为视图的职责是处理request,仅将提取的数据传递给服务层更为合理。
- 消息应由谁创建?我认为视图应负责创建消息,但视图无法获取商品名称,导致当前代码无法正常运行。
- 感觉现有代码架构存在问题,考虑让服务层返回商品名称而非状态码,请问该如何重构这段代码?
解答与重构方案
关于request传递的问题
不建议把request对象传给服务层。服务层要专注于业务逻辑处理,和HTTP请求解耦,这样业务逻辑才能在非HTTP场景(比如后台任务、单元测试)里复用。你的思路是对的,视图层负责解析request,提取必要参数(goods_id、user、addend)再传给服务层就够了。
关于消息创建的职责
视图层负责创建用户提示消息是合理的,因为消息属于用户交互环节,归视图层管。现在的问题是视图层拿不到商品名称,这可以通过让服务层返回更多上下文信息解决,而不是只返回状态码。
代码重构方案
让服务层返回包含操作状态、商品信息、操作类型的字典,视图层就能拿到足够信息生成消息。
重构后的service.py
def add_goods_to_cart(goods_id, user, addend): goods = Goods.objects.filter(pk=goods_id).first() result = { 'success': False, 'goods': None, 'action': None } if not goods: return result the_goods_already_in_cart = Cart.objects.filter(user=user, goods=goods, order=None).first() if the_goods_already_in_cart: new_quantity = the_goods_already_in_cart.quantity + addend if new_quantity <= 0: the_goods_already_in_cart.delete() result['action'] = 'removed' else: the_goods_already_in_cart.quantity = new_quantity the_goods_already_in_cart.save() result['action'] = 'updated' else: if addend > 0: # 仅加数量时创建新购物车项 Cart.objects.create(user=user, goods=goods, quantity=addend) result['action'] = 'added' else: # 商品不在购物车却要减数量,视为无效操作 return result result['success'] = True result['goods'] = goods return result
重构后的views.py
class AddToCart(LoginRequiredMixin, View): def post(self, request): goods_id = request.POST.get('goods_id') try: addend = int(request.POST.get('addend')) except (TypeError, ValueError): return HttpResponse("Invalid addend value", status=400) # 替换assert为显式校验,避免生产环境断言被禁用导致逻辑漏洞 if addend not in (1, -1): return HttpResponse("Addend must be 1 or -1", status=400) cart_result = add_goods_to_cart(goods_id, request.user, addend) if cart_result['success']: goods = cart_result['goods'] action_map = { 'added': 'added to cart', 'updated': 'quantity updated in cart', 'removed': 'removed from cart' } act = action_map[cart_result['action']] messages.add_message(request, messages.INFO, f'Goods "{goods.name}" {act}.') # 兜底处理HTTP_REFERER不存在的情况 return redirect(request.META.get('HTTP_REFERER', '/')) else: return HttpResponse("Wrong goods id or invalid operation", status=400)
重构说明
- 服务层返回包含
success(操作是否成功)、goods(商品对象)、action(具体操作类型)的字典,为视图层提供足够上下文生成用户消息。 - 视图层移除不安全的
assert,改用显式参数校验,避免生产环境下断言被禁用引发的问题。 - 增加
HTTP_REFERER不存在的兜底逻辑,防止重定向报错。 - 服务层处理了“商品不在购物车却要减数量”的边界情况,避免无效操作。
内容的提问来源于stack exchange,提问作者Kifsif
相关产品推荐
相关产品推荐

