如何在django-rest-framework中获取db验证错误后返回状态码?

时间:2017-07-23 00:43:39

标签: python django django-rest-framework

我有一个看起来像这样的测试用例(重点是防止重复的用户配置文件):

def test_create_duplicate_profile(self):
    new_user = models.User(
        username='bobjones',
        password=';alsdfkj;asoi'
    )
    new_user.save()
    client = APIClient()
    client.force_authenticate(new_user)
    response = client.post(
        path='/api/profiles/'
    )
    self.assertEqual(response.status_code, status.HTTP_201_CREATED)

    same_user = models.User.objects.get(
        username='bobjones'
    )
    response = client.post(
        path='/api/profiles/'
    )
    self.assertEqual(response.status_code, status.HTTP_409_CONFLICT)
    client.logout()

这会导致错误(以及测试失败):

(.virtualenv) nbascoutingdotcom $ python manage.py test profiles
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
E..
======================================================================
ERROR: test_create_duplicate_profile (profiles.tests.test_api.ProfilesAPITest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/evanzamir/nbascoutingdotcom/.virtualenv/lib/python3.6/site-packages/django/db/backends/utils.py", line 65, in execute
    return self.cursor.execute(sql, params)
  File "/Users/evanzamir/nbascoutingdotcom/.virtualenv/lib/python3.6/site-packages/django/db/backends/sqlite3/base.py", line 328, in execute
    return Database.Cursor.execute(self, query, params)
sqlite3.IntegrityError: UNIQUE constraint failed: profiles_profile.user_id

我的模型看起来像这样:

class Profile(TimestampModerated):
    id = models.BigAutoField(primary_key=True, db_column='id', null=False)
    uuid = models.UUIDField(db_index=True, default=uuid_lib.uuid4(), editable=False)
    user = models.OneToOneField('auth.User', on_delete=models.CASCADE, related_name='profiles', blank=False,
                                unique=True)
    bio = models.CharField(max_length=140, blank=True, null=True)
    media_url = models.URLField(blank=True, null=True)
    dob = models.DateField(blank=True, null=True)

    class Meta:
        verbose_name_plural = "profiles"

这是序列化器:

class ProfileSerializer(serializers.ModelSerializer):
    user = serializers.CharField(source='user.username', read_only=True,
                                 validators=[UniqueValidator(queryset=Profile.objects.all())])
    email = serializers.CharField(source='user.email', read_only=True)
    first = serializers.CharField(source='user.first_name', read_only=True)
    last = serializers.CharField(source='user.last_name', read_only=True)
    last_login = serializers.DateTimeField(source='user.last_login', read_only=True)
    date_joined = serializers.DateTimeField(source='user.date_joined', read_only=True)

    class Meta:
        model = Profile
        fields = ('id', 'created', 'moderation_code', 'user', 'updated', 'uuid', 'bio', 'email',
                  'first', 'last', 'last_login', 'media_url', 'dob', 'date_joined')

这是我的ViewSet:

class ProfileViewSet(viewsets.ModelViewSet):
    """
    This viewsetomatically provides `list`, `create`, `retrieve`,
    `update` and `destroy` actions.

    """
    queryset = Profile.objects.all()
    serializer_class = ProfileSerializer
    authentication_classes = (SessionAuthentication,)
    permission_classes = (permissions.AllowAny, IsOwnerOrReadOnly)
    lookup_field = 'user'

    def perform_create(self, serializer):
        serializer.save(user=self.request.user)

我希望能够发回有效的HTTP状态代码(HTTP_40X_),但我似乎无法挂钩验证错误。我尝试在perform_create方法中添加一些代码来引发ValidationError,但这对我不起作用。有什么建议吗?

1 个答案:

答案 0 :(得分:1)

您必须挂钩ModelViewSet.create(...),其中创建并返回实际响应:

from rest_framework import status
from rest_framework.response import Response


def create(self, request, *args, **kwargs):
    try:
        return super(ProfileViewSet, self).create(request, *args, **kwargs):
    except IntegrityError:
        return Response(status=status.HTTP_409_CONFLICT)

对于更一般的项目范围方法,您可能还会阅读drf docs on custom exception handling