如何在Django REST Framework中实现Transaction模型序列化及转账验证
解决DRF中交易序列化验证与账户余额更新问题
一、先完成Transaction模型与CustomUser的关联
首先要确保Transaction模型正确关联CustomUser,分别给sender和receiver设置反向关联名称,代码示例:
from django.db import models from django.contrib.auth import get_user_model CustomUser = get_user_model() class Transaction(models.Model): sender = models.ForeignKey(CustomUser, on_delete=models.CASCADE, related_name='sent_transactions') receiver = models.ForeignKey(CustomUser, on_delete=models.CASCADE, related_name='received_transactions') amount = models.DecimalField(max_digits=10, decimal_places=2) account_number = models.CharField(max_length=20) created_at = models.DateTimeField(auto_now_add=True) def __str__(self): return f"{self.sender.username} -> {self.receiver.username}: {self.amount}"
related_name='sent_transactions':让CustomUser实例可通过user.sent_transactions查看自己发起的所有交易related_name='received_transactions':让CustomUser实例可通过user.received_transactions查看自己收到的所有交易
二、编写带业务验证的序列化器
把参数验证、业务逻辑整合到序列化器中,重写validate做前置校验,重写create处理余额更新:
from rest_framework import serializers from .models import Transaction, Account from django.contrib.auth import get_user_model from decimal import Decimal CustomUser = get_user_model() class TransactionSerializer(serializers.ModelSerializer): # 前端只需传account_number和amount,sender由当前登录用户自动填充 account_number = serializers.CharField(write_only=True) amount = serializers.DecimalField(max_digits=10, decimal_places=2) class Meta: model = Transaction fields = ['account_number', 'amount', 'sender', 'receiver'] extra_kwargs = { 'sender': {'read_only': True}, 'receiver': {'read_only': True} } def validate(self, attrs): amount = attrs.get('amount') account_number = attrs.get('account_number') user = self.context['request'].user # 验证金额合法性 if amount <= Decimal('0'): raise serializers.ValidationError("转账金额必须大于0") # 验证接收方账户存在 try: receiver_account = Account.objects.get(account_number=account_number) except Account.DoesNotExist: raise serializers.ValidationError("接收方账户不存在") # 验证发起方账户存在且余额充足 try: sender_account = Account.objects.get(user=user) except Account.DoesNotExist: raise serializers.ValidationError("发起方账户不存在") if sender_account.account_balance < amount: raise serializers.ValidationError("账户余额不足") # 把验证通过的账户信息存入attrs,供create方法使用 attrs['sender_account'] = sender_account attrs['receiver_account'] = receiver_account attrs['receiver'] = receiver_account.user attrs['sender'] = user return attrs def create(self, validated_data): # 取出验证后的关键数据 sender_account = validated_data.pop('sender_account') receiver_account = validated_data.pop('receiver_account') amount = validated_data['amount'] interest_rate = Decimal('0.02') deduction = amount * interest_rate # 开启事务,确保所有数据库操作原子性 from django.db import transaction with transaction.atomic(): # 更新发起方余额 sender_account.account_balance -= amount sender_account.save() # 更新接收方余额 receiver_account.account_balance += amount receiver_account.save() # 更新超级用户账户余额(建议把超级用户账号存入配置,不要硬编码) superuser = CustomUser.objects.get(username='superuser username') superuser_account = Account.objects.get(user=superuser) superuser_account.account_balance += deduction superuser_account.save() # 创建交易记录 transaction_obj = Transaction.objects.create(**validated_data) return transaction_obj
三、改造DRF视图
简化视图逻辑,让序列化器处理核心业务,视图仅负责请求分发:
from rest_framework.decorators import api_view from rest_framework.response import Response from rest_framework import status from .serializers import TransactionSerializer @api_view(['POST']) def create_transaction(request): serializer = TransactionSerializer(data=request.data, context={'request': request}) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
关键说明
- 序列化器校验逻辑:把所有验证(金额合法性、账户存在性、余额充足性)集中在
validate方法,避免视图代码臃肿 - 事务原子性:用
transaction.atomic()包裹所有数据库写操作,防止出现部分更新成功的异常情况,保证数据一致性 - 上下文传递:将request传入序列化器的context,在序列化器中直接获取当前登录用户,无需视图手动处理
- 前端传参简化:前端只需传递
account_number和amount,sender和receiver由后端自动填充,降低前端出错概率
内容的提问来源于stack exchange,提问作者Bubble
相关产品推荐
相关产品推荐

