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

Django生成优惠券报AssertionError:request为NoneType的问题求助

问题描述

我正在开发一个功能:当客户下单金额达到指定额度时自动生成优惠券作为奖励,但目前持续报错:AssertionError: The request argument must be an instance of django.http.HttpRequest, not builtins.NoneType。订单能成功创建,但优惠券始终生成失败。

相关代码

views.py

@api_view(['POST'])
def coupon_code_generator(request):
    coupon_amount = request.data.get('coupon_amount')
    print("The coupon amount is ", coupon_amount)
    serializer = CouponSerializer(data=request.data)

    if serializer.is_valid():
        serializer.save(user=request.user)
        qset = Coupon.objects.filter(user=request.user)
        customer_name = qset[0].user.first_name
        phone_number = qset[0].coupon_order.phone_number
        coupon_code = qset[0].coupon_code

        if(phone_number.startswith("+254")):
            pn = phone_number
        elif(phone_number.startswith('0')):
            pn = re.sub("0","+254",phone_number,1)
        elif (phone_number.startswith('7') or phone_number.startswith('1')):
            pn = "+254"+phone_number
        elif(phone_number.startswith('254')):
            pn = "+"+phone_number
                sms.send(f'Dear {customer_name},as a token of our appreciation, we are thrilled to present you with an exclusive coupon code to use on your next purchase. The code is {coupon_code} and is worth {coupon_amount}',[f'{pn}'],callback=coupon_code_generator)
        return Response(serializer.data)
    else:
        return Response('Coupon not generated')

models.py

class Coupon(models.Model):
    coupon_code = models.CharField(default=coupon_generator,primary_key=True,max_length=100)
    coupon_amount = models.DecimalField(max_digits=8, decimal_places=2)
    coupon_order = models.ForeignKey(Order,related_name='coupon_order',on_delete=models.CASCADE)
    user = models.ForeignKey(UserModel,related_name='user', on_delete=models.CASCADE)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ('-created_at',)

    def __str__(self):
        return f"{self.coupon_order.email}({'%s' % self.coupon_order.id}) - {'%s' % self.coupon_code}"

serializers.py

class CouponSerializer(serializers.ModelSerializer):
    coupon_order = serializers.PrimaryKeyRelatedField(queryset = Order.objects.all())
    user = serializers.ReadOnlyField(source='user.email')
    class Meta:
        model = Coupon
        fields=['coupon_code','coupon_amount','user','coupon_order','created_at']
错误分析与解决方法

核心错误原因

报错的本质是**request参数为None**,问题出在sms.send()的callback=coupon_code_generator这一行:

  • coupon_code_generator是被@api_view装饰的视图函数,强制要求第一个参数必须是合法的HttpRequest实例
  • 当sms.send()触发回调时,不会传入有效的HttpRequest对象,直接传入None,触发断言失败

此外还有两个潜在问题:

  1. 用Coupon.objects.filter(user=request.user)取第一个对象,可能拿到旧优惠券,而非刚创建的
  2. 手机号处理分支未覆盖所有情况,部分号码会导致短信无法发送

具体修复步骤

1. 拆分短信发送逻辑,移除视图函数作为回调

把短信处理抽成独立函数,避免直接调用视图函数:

def send_coupon_notification(customer_name, coupon_code, coupon_amount, phone_number):
    # 统一处理手机号格式
    if phone_number.startswith("+254"):
        pn = phone_number
    elif phone_number.startswith('0'):
        pn = re.sub("0", "+254", phone_number, 1)
    elif phone_number.startswith('7') or phone_number.startswith('1'):
        pn = "+254" + phone_number
    elif phone_number.startswith('254'):
        pn = "+" + phone_number
    else:
        print(f"无效手机号格式:{phone_number}")
        return
    
    sms_content = f"Dear {customer_name}, as a token of our appreciation, we are thrilled to present you with an exclusive coupon code to use on your next purchase. The code is {coupon_code} and is worth {coupon_amount}"
    sms.send(sms_content, [pn])

修改视图函数,直接使用保存后的优惠券实例:

@api_view(['POST'])
def coupon_code_generator(request):
    coupon_amount = request.data.get('coupon_amount')
    serializer = CouponSerializer(data=request.data)

    if serializer.is_valid():
        # 直接获取刚创建的优惠券实例
        coupon = serializer.save(user=request.user)
        # 调用独立的短信发送函数
        send_coupon_notification(
            coupon.user.first_name,
            coupon.coupon_code,
            coupon_amount,
            coupon.coupon_order.phone_number
        )
        return Response(serializer.data)
    else:
        return Response('Coupon not generated')

2. 优化序列化器的用户字段处理

可以在序列化器中自动注入当前用户,避免视图中手动传参:

class CouponSerializer(serializers.ModelSerializer):
    coupon_order = serializers.PrimaryKeyRelatedField(queryset=Order.objects.all())
    user = serializers.ReadOnlyField(source='user.email')
    
    class Meta:
        model = Coupon
        fields = ['coupon_code', 'coupon_amount', 'user', 'coupon_order', 'created_at']
    
    def create(self, validated_data):
        # 从请求上下文获取当前用户
        validated_data['user'] = self.context['request'].user
        return super().create(validated_data)

此时视图中只需调用coupon = serializer.save()即可。

3. 确保自动触发逻辑的合法性

如果该功能是订单创建后自动触发,需保证调用方传入了合法的HttpRequest对象;若使用后台任务(如Celery)执行优惠券生成,则不能用@api_view装饰的视图,应改用普通函数处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 01:27:47