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

如何在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)

关键说明

  1. 序列化器校验逻辑:把所有验证(金额合法性、账户存在性、余额充足性)集中在validate方法,避免视图代码臃肿
  2. 事务原子性:用transaction.atomic()包裹所有数据库写操作,防止出现部分更新成功的异常情况,保证数据一致性
  3. 上下文传递:将request传入序列化器的context,在序列化器中直接获取当前登录用户,无需视图手动处理
  4. 前端传参简化:前端只需传递account_number和amount,sender和receiver由后端自动填充,降低前端出错概率

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 09:57:23