Serializerによる検証と保存


フィードバック機能付き完全なPython/張庫Webサービス開発ガイド課の後で整理した文章を聞き終わった.
Serializerを使用して検証してデータベースに保存する手順と、非Serializerが注目するフィールドをビューでどのように処理するかについて説明します.
1.Serializerの作成者
SerializerはDjango Formの概念/使い方と似ていますが、作成者は異なります
# django/forms/forms.py
class BaseModelForm:
    def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, 
                initial=None, error_class=ErrorList, label_suffix=None, 
                empty_permitted=False, instance=None, use_required_attribute=None):

class Form(BaseForm):
    pass
# rest_framework/serializers.py
class BaseSerializer(Field):
    def __init__(self, instance=None, data=empty, **kwargs):

# instance -> 직렬화 목적
# data -> 유효성 검사

class Serializer(BaseSerializer):
    pass
2.指定されたdata=パラメータの場合
呼び出しまで.is valid()
  • .initial dataフィールド
  • にアクセスできます.
  • .validate data(フォームではcleane data)で検証された値.save()で使用します.
  • .errors→検証後のエラー履歴
  • .データ→検証後、更新されたインスタンスのフィールド値辞書
  • 3. serializer.save(**kwargs)を呼び出すと
  • DBに保存する関連インスタンス
  • を返す.
  • .validated dataとworgs辞書をマージ
  • .update関数create関数を使用して、関連フィールドに値を割り当て、データベース
  • に保存しようとします.
  • .update() : self.instanceパラメータ指定時は
  • .create() : self.instanceパラメータが指定されていない場合は
  • # Form에서의 save코드
    form = PostForm(request.POST)
    if form.is_valid():
        post = form.save(commit=False)
        post.author = request.user
        post.ip = request.META['REMOTE_ADDR']
        post.save()
    
    # Serializer에서의 save코드
    serializer.is_valid(...) # raise
    serializer.save(author=request.user, ip=request.META['REMOTE_ADDR'])
    4. from rest_framework.validators import ...
    https://github.com/encode/django-rest-framework/blob/master/rest_framework/validators.py
    値検証を実行する呼び出し可能オブジェクト
    DRFはValidatorsを提供し、一意性の検査を支援します.
  • UniqueValidator:指定されたQuerySet範囲内の指定されたフィールドの一意性を確認する
  • UniqueTogetherValidator:UniqueValidatorのマルチフィールドバージョン
  • BaseUniqueForValidator
  • UniqueForDataValidator(BaseUniqueValidator):指定した日付範囲内の一意性を確認します.
  • UniqueForMonthValidator(BaseUniqueValidator):指定した月の範囲内の一意性を確認します.
  • UniqueForYearValidator(BaseUniqueValidator):指定した年の範囲内の一意性を確認します.
    4.1. UniqueValidator
    [モデル](Model)フィールドで[ユニーク=リアル](Unique=Real)を指定すると自動的に指定されます.
  • queryset(必須):ターゲット・クエリー・セットの範囲
  • メッセージ:検証に失敗したときのエラーメッセージ
  • lookup:depolt“exact”
  • Serializerフィールドにベリファイアを直接指定できます
    from rest_framework.validators import UniqueValidator
    
    slug = SlugField(
        max_length=100,
        validators=[UniqueValidator(queryset=BlogPost.objects.all())]
    )
    ◇モデルフィールドにunique=Trueを指定し、モデルSerilizerにUniqueValidatorを追加することを推奨
    4.2. UniqueTogetherValidator
    モデルmetaに一意のtotalが指定されている場合は、自動的に指定されます.
  • queryset(必須):ターゲット・クエリー・セットの範囲
  • フィールド(必須フィールド)
  • message
  • from rest_framework.validators import UniqueTogetherValidator
    class ExampleSerializer(serializers.Serializer):
        # ...
        class Meta:
            validators = [
                UniqueTogetherValidator(
                    queryset=ToDoItem.objects.all(),
                    fields=['list', 'position']
                )
            ]
    4.3. UniqueForDateValidator/Month/Year
    特定のクエリー・セット→特定の日付範囲→特定のフィールドがユニークかどうかを確認します.
  • queryset(必須):ターゲット・クエリー・セットの範囲
  • フィールド(必須フィールド)
  • date field(必須フィールド):Date、Month、Yearの値が参照される日付フィールド(フィールドの年/月/日Validatorを参照).
  • message
  • from rest_framework.validators import UniqueTogetherValidator
    class ExampleSerializer(serializers.Serializer):
        # ...
        class Meta:
            validators = [
                UniqueForYearValidator( 
                    queryset=BlogPostItem.objects.all(), 
                    field='slug', 
                    date_field='published'
                ) 
            ]
    5.検証に失敗した場合ValidationError異常
    https://github.com/encode/django-rest-framework/blob/3.10.1/rest_framework/exceptions.py#L138
    rest frameworkでなければなりません.exception.ValidationErrorの使用
    ドラムベースではdjango.forms.exceptions.ValidationError
    class ValidationError(APIException):
        status_code = status.HTTP_400_BAD_REQUEST
        default_detail = _('Invalid input.')
        default_code = 'invalid'
    
        def __init__(self, detail=None, code=None):
            if detail is None:
                detail = self.default_detail
            if code is None:
                code = self.default_code
    
            if not isinstance(detail, dict) and not isinstance(detail, list):
                detail = [detail]
    
            self.detail = _get_error_details(detail, code)
    6.Serializerでの検証
    フィールドを定義するときにvalidators、またはクラスmetaを指定します.ベリファイアの指定
    オンサイト・レベルのチェック:値の検証と変換
    class PostSerializer(serializers.Serializer):
        title = serializers.CharField(max_length=100)
    
        def validate_title(self, value): # validator_필드명을 구현해서 유효성 검사를 진행할 수 있다. (form에서는 clean_필드명)
            if 'django' not in value:
                raise ValidationError('제목에 필히 django가 포함되어야합니다.')  # 조건에 맞지않으면 rest_framework의 validationError를 발생시킨다. (form에 있는 validationError랑 헷갈리면 안됨)
            return value
    ≪オブジェクト・レベルのチェック|Object Level Check|ldap≫:値の検証と変換
    class PostSerializer(serializers.Serializer):
        title = serializers.CharField(max_length=100)
    
        def validate(self, data):  # 2개 이상의 필드에 대해 유효성 검사 로직은 validate를 구현하면 된다.(form에서는 clean)
            if 'django' not in data['title']:
                raise ValidationError('제목에 필히 django가 포함되어야합니다.')
            return data
    7.DBおよびMixinsに反映されるperform系列関数
    APIViewのcreate/update/stroyメンバー関数では、実際のDB処理ロジックは次のようになります.(ミキシングで定義されます.)
  • perform_create(serializer)
  • perform_update(serializer)
  • perform_destroy(instance)
  • class CreateModelMixin:
        def create(self, request, *args, **kwargs):
            serializer = self.get_serializer(data=request.data)
            serializer.is_valid(raise_exception=True)
            self.perform_create(serializer)
            headers = self.get_success_headers(serializer.data)
            return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
    
        def perform_create(self, serializer):
            serializer.save()  # 모델 객체가 만들어지고, 모델 객체의 값들이 할당이 다 된 model.save()가 호출이 되는 것이다.
    
        def get_success_headers(self, data):
            try:
                return {'Location': str(data[api_settings.URL_FIELD_NAME])}
            except (TypeError, KeyError):
                return {}
    createで他に保存するフィールドがある場合は?
  • モデルにはipフィールドがありますが、ユーザーのipeを保存したい場合は?
  • # CreateModelMixin에서 상속받은 뷰에서 아래와 같이 사용한다.
    
    def perform_create(self, serializer):  # 실제 APIView구현에서 
        serializer.save(ip=self.request.META['REMOTE_ADDR'])