如何从Django REST Framework序列化器返回自定义JsonResponse?
解决Django REST Framework返回自定义响应并调用外部API的方案
核心思路
ModelViewSet的默认update方法会返回序列化器处理后的模型实例数据,要实现自定义响应,需要重写视图的update方法,在其中完成:
- 执行原有业务逻辑(创建交易、通知)
- 调用外部API并处理响应
- 构造包含自定义内容和外部API响应的JSON返回
具体代码修改
1. 修改序列化器(保存业务数据引用)
在序列化器的update方法中,将创建的交易和通知实例保存到序列化器对象中,方便后续视图获取:
from rest_framework import serializers from rest_framework.exceptions import PermissionDenied from django.utils import timezone from .models import User, BankingTransaction, Notifications class SendMoneyExternalSerializer(serializers.ModelSerializer): def update(self, instance, validated_data): # 权限验证 requesting_user = str(self.context['request'].user) if requesting_user != str(instance.username) and requesting_user not in instance.userprofile.banking_write: raise PermissionDenied('You do not have permission to update') # 提取请求参数 account_number = self.context['request'].data['to_account'] amount = self.context['request'].data['amount'] to_bank = self.context['request'].data['to_bank'] description = self.context['request'].data['description'] trans_reference = self.context['request'].data['trans_reference'] status = self.context['request'].data['status'] # 创建交易记录并保存到序列化器实例 self.transaction = BankingTransaction.objects.create( user=instance, date=timezone.now(), from_account_num=instance.userprofile.account_number, from_account_name=instance.userprofile.company, to_account=account_number, to_bank=to_bank, amount_transferred=amount, description=description, trans_reference=trans_reference, status=status, ) # 更新通知计数 instance.userprofile.notification_count += 1 instance.userprofile.save() # 创建通知并保存到序列化器实例 self.notification = Notifications.objects.create( user=instance, date=timezone.now(), read=False, message=f'You have paid N{amount} to account number {account_number} ({to_bank})' ) return instance class Meta: model = User fields = ['pk']
2. 重写视图的update方法(处理外部API和自定义响应)
在视图中重写update方法,完成外部API调用并构造自定义响应:
from django.http import JsonResponse import requests from rest_framework import status from rest_framework.viewsets import ModelViewSet from .models import User from .serializers import SendMoneyExternalSerializer class sendmoney_external(ModelViewSet): queryset = User.objects.all() serializer_class = SendMoneyExternalSerializer lookup_field = 'username' def update(self, request, *args, **kwargs): # 获取目标用户实例 instance = self.get_object() # 初始化序列化器并执行业务逻辑 serializer = self.get_serializer(instance, data=request.data, partial=True) serializer.is_valid(raise_exception=True) serializer.save() # ---------------------- 调用外部API ---------------------- # 替换为实际外部API地址 external_api_url = "https://your-external-api-domain.com/transaction-endpoint" # 构造外部API请求参数(根据API要求调整) external_payload = { "from_account": instance.userprofile.account_number, "to_account": request.data.get('to_account'), "amount": request.data.get('amount'), "bank_code": request.data.get('to_bank'), "reference": request.data.get('trans_reference') } try: # 发送请求到外部API(根据API要求调整请求方法,如GET/PUT) external_response = requests.post( external_api_url, json=external_payload, # 如果API需要认证,添加headers # headers={"Authorization": "Bearer YOUR_API_KEY"}, timeout=10 ) external_response.raise_for_status() # 触发HTTP错误异常 external_api_data = external_response.json() except requests.exceptions.RequestException as e: # 处理外部API调用失败的情况 return JsonResponse( { "status": "failed", "message": f"External API call failed: {str(e)}", "internal_transaction_id": serializer.transaction.id }, status=status.HTTP_503_SERVICE_UNAVAILABLE ) # ---------------------- 构造自定义响应 ---------------------- custom_response = { "status": "success", "message": "Transaction processed successfully", "internal_transaction": { "id": serializer.transaction.id, "amount": serializer.transaction.amount_transferred, "to_account": serializer.transaction.to_account, "created_at": serializer.transaction.date.isoformat() }, "external_api_result": external_api_data } return JsonResponse(custom_response, status=status.HTTP_200_OK)
关键注意事项
- 先安装
requests库:pip install requests - 外部API的请求方法、参数、认证方式需根据实际文档调整
- 异常处理要覆盖超时、HTTP错误、JSON解析失败等场景
- 如果需要更严谨的权限验证,可以将权限逻辑移到视图的
check_object_permissions方法中 - 自定义响应的字段可根据业务需求自由增减,比如添加通知详情、用户信息等
内容的提问来源于stack exchange,提问作者Kyle Andrew
相关产品推荐
相关产品推荐

