You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)

重构说明

  1. 服务层返回包含success(操作是否成功)、goods(商品对象)、action(具体操作类型)的字典,为视图层提供足够上下文生成用户消息。
  2. 视图层移除不安全的assert,改用显式参数校验,避免生产环境下断言被禁用引发的问题。
  3. 增加HTTP_REFERER不存在的兜底逻辑,防止重定向报错。
  4. 服务层处理了“商品不在购物车却要减数量”的边界情况,避免无效操作。

内容的提问来源于stack exchange,提问作者Kifsif

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 02:13:15