2015-11-02 10 views
9

ユーザーにアカウントを登録できるAPIエンドポイントがあります。重複したユーザー名に対して400ではなくHTTP 409を返したいと思います。エラーがトリガされるとDjango REST Frameworkシリアライザで検証エラーを発生させる際にエラーコードを指定する方法

from django.contrib.auth.models import User 
from rest_framework.serializers import ModelSerializer 

class UserSerializer(ModelSerializer): 
    username = CharField() 

    def validate_username(self, value): 
     if User.objects.filter(username=value).exists(): 
      raise NameDuplicationError() 
     return value 


class NameDuplicationError(APIException): 
    status_code = status.HTTP_409_CONFLICT 
    default_detail = u'Duplicate Username' 

、応答は次のとおりです:{"detail":"Duplicate Username"}

は、ここに私のシリアライザです。 APIExceptionをサブクラス化すると、usernameの代わりにdetailというキーが使用されることに気付きました。

は、私が代わりに{"username":"Duplicate Username"}

この応答を持つようにしたいか、私はValidationErrorをを上げるステータスコードを指定したいと思います:

def validate_username(self, value): 
    if User.objects.filter(username=value).exists(): 
     raise serializers.ValidationError('Duplicate Username', 
              status_code=status.HTTP_409_CONFLICT) 
    return value 

をしかしValidationErrorのみ400

を返すので、これは動作しません。

これを達成する他の方法はありますか?

答えて

13

あなたは次のように別の例外を発生させることができます。

from rest_framework.exceptions import APIException 
from django.utils.encoding import force_text 
from rest_framework import status 


class CustomValidation(APIException): 
    status_code = status.HTTP_500_INTERNAL_SERVER_ERROR 
    default_detail = 'A server error occurred.' 

    def __init__(self, detail, field, status_code): 
     if status_code is not None:self.status_code = status_code 
     if detail is not None: 
      self.detail = {field: force_text(detail)} 
     else: self.detail = {'detail': force_text(self.default_detail)} 
あなたのようなあなたのシリアライザでこれを使用することができます

raise CustomValidation('Duplicate Username','username', status_code=status.HTTP_409_CONFLICT) 

または

raise CustomValidation('Access denied','username', status_code=status.HTTP_403_FORBIDDEN) 
3

使用ジャンゴ・休息・フレームワークのカスタム例外ハンドラhttp://www.django-rest-framework.org/api-guide/exceptions/

def custom_exception_handler(exc, context=None): 
    response = exception_handler(exc, context) 
    if response is not None: 
     if response.data['detail'] == 'Duplicate Username': 
      response.data['username'] = response.data.pop('detail') 
     response.status_code = status.HTTP_409_CONFLICT 
    return response 
+0

素晴らしいです。異なるビューの例外をカスタマイズしたい場合、この関数は非常に複雑になります。ビューの数が増えると、この関数も同様に増加します。私はスケーラブルな解決策があるのだろうかと思います。 – Cheng

+0

「詳細」を上書きすることを意味していますか? –

+0

私がオーバーライドしたい3つの例外がある場合は、 'custom_exception_handler'にif..elif..elif構造体があります。私がカスタマイズしたい例外が増えるほど、このメソッドで書く必要が増えます。また、どのビューのどのHTTPアクションがブランチであるかを覚えておく必要があります。これは本物の問題となる可能性があります。 – Cheng

関連する問題