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,触发断言失败
此外还有两个潜在问题:
- 用
Coupon.objects.filter(user=request.user)取第一个对象,可能拿到旧优惠券,而非刚创建的 - 手机号处理分支未覆盖所有情况,部分号码会导致短信无法发送
具体修复步骤
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
相关产品推荐
相关产品推荐

