You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Django REST框架中捕获处理唯一约束失败错误以通过测试

解决Django REST Framework中unique_together约束的错误处理问题

嘿,我完全懂你现在的困扰——作为DRF新手,碰到数据库层面的约束错误不知道怎么转换成优雅的响应,还导致测试卡壳,确实挺头疼的。咱们一步步来搞定这个问题:

1. 先在序列化器里提前做唯一性验证(最优方案)

数据库的IntegrityError是底层异常,DRF默认不会把它转换成用户友好的响应。所以最好的办法是在序列化器的验证阶段就提前拦截,避免触发数据库错误。

修改你的serializers.py,添加自定义的validate方法:

from rest_framework import serializers
from .models import Review
from django.contrib.auth import get_user_model

User = get_user_model()

class ReviewSerializer(serializers.ModelSerializer):
    target = serializers.PrimaryKeyRelatedField(queryset=User.objects.all())
    author = serializers.ReadOnlyField(source='author.id')

    class Meta:
        model = Review
        fields = ['id', 'feedback', 'review', 'target', 'author', 'created']

    def validate(self, data):
        # 从请求上下文里拿到当前登录的用户(也就是评论的作者)
        author = self.context['request'].user
        target = data['target']
        
        # 检查该作者是否已经给目标用户留过评论
        if Review.objects.filter(author=author, target=target).exists():
            raise serializers.ValidationError("你已经给这个用户提交过评论啦!")
        
        return data

这样一来,当用户重复提交时,序列化器会直接抛出DRF标准的ValidationError,视图会自动返回400 Bad Request响应,不会走到数据库层面。

2. 给视图加兜底的异常捕获(处理并发场景)

虽然序列化器验证能处理大部分情况,但如果碰到高并发场景——比如两个请求同时通过了序列化器验证,几乎同时写入数据库——这时候还是会触发数据库的UNIQUE约束错误。所以我们需要在视图里捕获这个异常,转换成友好响应。

修改你的views.py,调整perform_create方法:

from django.db import IntegrityError
from rest_framework import serializers
from rest_framework import viewsets
from .models import Review
from .serializers import ReviewSerializer
from .permissions import ReviewPermissions

class ReviewViewSet(viewsets.ModelViewSet):
    queryset = Review.objects.all()
    serializer_class = ReviewSerializer
    permission_classes = [ReviewPermissions]

    def perform_create(self, serializer):
        try:
            serializer.save(author=self.request.user)
        except IntegrityError:
            # 捕获唯一约束异常,抛出DRF的验证错误
            raise serializers.ValidationError("你已经给这个用户提交过评论啦!")

3. 调整测试用例,验证预期的响应

现在错误处理好了,测试用例就可以正常验证重复提交的场景了,不用再担心抛出未处理的IntegrityError。举个测试示例:

from django.test import TestCase
from rest_framework.test import APIClient
from django.contrib.auth import get_user_model

User = get_user_model()

class ReviewUniqueConstraintTest(TestCase):
    def setUp(self):
        self.client = APIClient()
        # 创建测试用户
        self.author = User.objects.create_user(username="test_author", password="123456")
        self.target = User.objects.create_user(username="test_target", password="654321")
        # 先登录作者用户,创建一条评论
        self.client.force_authenticate(user=self.author)
        self.client.post("/api/reviews/", {
            "feedback": "POSITIVE",
            "review": "这家店超棒!",
            "target": self.target.id
        })

    def test_duplicate_review_returns_400(self):
        # 再次提交相同作者和目标的评论
        response = self.client.post("/api/reviews/", {
            "feedback": "NEGATIVE",
            "review": "体验很差!",
            "target": self.target.id
        })
        
        # 断言返回400错误,且包含正确的错误信息
        self.assertEqual(response.status_code, 400)
        self.assertIn("你已经给这个用户提交过评论啦!", str(response.data))

这样调整后,你的测试就能顺利通过,同时用户也能收到清晰的错误提示,而不是晦涩的数据库异常信息。

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

火山引擎 最新活动