[Django REST Framework]シリアルライザのクリーンアップ


Serializers:クエリーセットやモデルインスタンスなどの複雑なデータをJSON、XMLなどのコンテンツタイプに変換し、逆にJSON、XMLなどのデータをクエリーセットやモデルインスタンスに変換します.(deserialize)

serializer.is_valid()


create、updateなどの逆シーケンス化プロセスが必要な場合、検証用の関数を呼び出す必要があります.
  • モデルで指定するフィールドタイプとフィールド条件(Unique=True、blank=Falseなど)のデータ
  • が満たされているかどうかを確認します.
  • 条件を満たす場合、validate dataに有効性を通過するデータを加える.
  • 以降のシーケンサ.save()はリソースを作成または更新します.
  • serializer.is valid()エラーの作成


    データが検証に失敗した場合、エラー応答が必要になりますが、簡単に次の内容を作成した場合、検証に失敗した場合、メッセージなしで500(内部サーバエラー)ステータスコードが返されます.
    serializer = RegisterSerializer(data=request.data)
    serializer.is_valid()
    serializer.save()
    return Response({'message': message.REGISTER_SUCCESS}, status=status.HTTP_201_CREATED)
    
  • if文を用いたエラー処理
  • 
    serializer = RegisterSerializer(data=request.data)
    if serializer.is_valid():
    	serializer.save()
    	return Response({'message': message.REGISTER_SUCCESS}, status=status.HTTP_201_CREATED)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    上記のコードに示すように、if文を使用して検証に失敗した場合にエラー処理を行うことができます.
    serializer.errorsによってkey、value形式の有効性フィールドおよびその理由(ex.userwith this emailは既に存在する)を介して応答メッセージに送信できません.
  • raise exceptionを用いたエラー処理
  • serializer = RegisterSerializer(data=request.data)
    serializer.is_valid(raise_exception=True)
    serializer.save()
    return Response({'message': message.REGISTER_SUCCESS}, status=status.HTTP_201_CREATED)
    上記のコードのように、serializer.is_valid(raise_exception=True)を使用すると、コードを減らしてエラー処理を行うことができます.検証に失敗した場合は、独自にシーケンス化できます.errorsとともに、ステータスコードも400コードを返し、指定する必要はありません.

    serializer.save()


    検証されたデータ(validate data)はシーケンス化器です.リソースが存在しない場合はsave()に移動し、存在する場合はupdate()に移動します.
    # serializer.save() 내부 코드 중 일부
    # 기존에 존재하는 리소스이면 update, 아니면 create로 처리
    
            if self.instance is not None:
                self.instance = self.update(self.instance, validated_data)
                assert self.instance is not None, (
                    '`update()` did not return an object instance.'
                )
            else:
                self.instance = self.create(validated_data)
                assert self.instance is not None, (
                    '`create()` did not return an object instance.'
                )
    create、update関数は、追加条件を上書きすることができます.
    from django.contrib.auth.hashers import make_password
    
    class RegisterSerializer(serializers.ModelSerializer):
        security_code = SecurityCodeSerializer(read_only=True)
        password2 = serializers.CharField(max_length=128, read_only=True)
        class Meta:
            model = User
            fields = [
                'id',
                'email',
                'password',
                'password2',
                'nickname',
                'name',
                'phone',
                'security_code'
            ]
        def create(self, validated_data):
            validated_data['password'] = make_password(validated_data['password'])
            return super().create(validated_data)
    以上のように、Serializersクラスは、リソースの作成時にcreate関数を作成し、条件を追加します.(パスワードが復号されて保存される)、update関数も同様に変更されます.
  • 差異
  • create()はvalidate dataパラメータを受け入れます.
  • update()は、インスタンスとvalidate dataの2つのパラメータを受信し、インスタンスを返します.(既存のリソースを更新する必要があるインスタンス)
  •     
        #update 예시
        def update(self, instance, validated_data):
            new_password = make_password(validated_data['password'])
            instance.password = new_password
            instance.save()
            return instance

    validation


    is valid()で検証するほか、カスタムで検証することもできます.まず、モデルで指定したフィールドタイプ/条件を順番に検証し、カスタム検証を行います.

    object_level validation


    複数のフィールドにアクセスする必要がある場合に使用する検証は、validate()関数で作成できます.
    class ResetPasswordSerializer(serializers.ModelSerializer):
       security_code = SecurityCodeSerializer(read_only=True)
       password2 = serializers.CharField(read_only=True)
       class Meta:
           model = User
           fields = [
               'security_code',
               'email',
               'password',
               'password2'
           ]
    
       def validate(self, data):
           input_password = data['password']
           input_email = data['email'].split("@")[0]
           nickname = User.objects.get(email=data['email']).nickname
           if not 7 < len(input_password) < 13:
               raise serializers.ValidationError(message.PASSWORD_LENGTH_ERROR)
    
           if not any(char.isdigit() for char in input_password):
               raise serializers.ValidationError(message.PASSWORD_COMBINATION_ERROR)
    
           if not any(char.isalpha() for char in input_password):
               raise serializers.ValidationError(message.PASSWORD_COMBINATION_ERROR)
    
           if input_password in input_email \
                   or input_email in input_password:
               raise serializers.ValidationError(message.PASSWORD_UNIQUE_ERROR)
    
           if input_password in nickname \
                   or nickname in input_password:
               raise serializers.ValidationError(message.PASSWORD_UNIQUE_ERROR)
           return data
    前述したように、パスワードリセットの検証が必要であり、複数のフィールドにアクセスする必要がある場合、validate()関数を使用してobject-levelの検証を作成できます.

    field_level validation


    1つのフィールドに対して有効性チェックを行う場合、field-level有効性チェックはvalidate_필드명(self, value)形式で行うことができます.
    class PhoneVerifySerializer(serializers.ModelSerializer):
       class Meta:
           model = PhoneVerification
           fields = [
               'id',
               'phone',
               'security_code'
           ]
    
       def validate_phone(self, value):
           if any(num.isalpha() for num in value):
               raise serializers.ValidationError(message.PHONE_NUMBER_ERROR)
           return value
    
    field-levelなので、パラメータとして受信した値はphoneデータです.