DRF에서의 시리얼라이즈 기능과 활용 방법


"""
1. APIView 요청 처리 흐름
   1) View 클래스 상속 및 기본 기능 포함
   2) as_view, dispatch 메서드 재정의 및 클래스 속성 구성
      as_view: CSRF 인증 제한 설정
      dispatch: 요청 처리, 데이터 파싱, 인증 처리, 예외 처리, 응답 생성
      클래스 속성: 전역/지역 설정 구성

2. 요청 모듈
   1) request 객체는 request._request와 호환됨
   2) request.query_params: URL 쿼리 파라미터 | request.data: 본문 데이터 추출

3. 파싱 모듈
   1) 허용되는 데이터 형식 설정 (form-data, urlencoded, json)

4. 응답 모듈
   1) Response(data, status, exception) 사용

5. 렌더링 모듈
   1) 클라이언트에게 전달할 데이터 포맷 설정 (json, browser)

6. 예외 모듈
   1) 커스텀 예외 처리 함수 구현: 클라이언트 오류 처리 또는 DRF 자체 처리
      처리 결과가 null이면 서버 오류로 간주하고 로그에 기록
   2) settings.py에서 커스텀 예외 처리 함수 설정
"""

시리얼라이저 가족 -----

"""
1. Serializer 클래스: 밑단 시리얼라이저 (기초 지식)
   주요: 단일 테이블 시리얼라이징

2. ModelSerializer 클래스: 모델 시리얼라이저 (핵심 클래스)
   주요: 다중 테이블 시리얼라이징

3. ListSerializer 클래스: 집합 작업 시리얼라이저 (보조 클래스)
   주요: 단일/다중 테이블 집합 추가/수정 작업 보조
"""

Serializer 시리얼라이징(기초 지식) ----------------- ##### 뷰 클래스

"""
# 뷰 클래스 시리얼라이징 과정
# 1) ORM으로 데이터 조회
# 2) 시리얼라이저를 통해 클라이언트에게 전달 가능한 데이터 변환
# 3) 데이터 클라이언트에 반환
"""
from . import serializers
class UserV2APIView(APIView):
    # 단일 조회 / 다중 조회
    def get(self, request, *args, **kwargs):
        pk = kwargs.get('pk')
        if pk:
            user_obj = models.User.objects.filter(is_delete=False, pk=pk).first()
            if not user_obj:
                return Response({
                    'status': 1,
                    'msg': 'pk error',
                }, status=400)

            serializer_instance = serializers.UserSerializer(user_obj, many=False)  # many 기본값 False
            user_data = serializer_instance.data
            return Response({
                'status': 0,
                'msg': 'ok',
                'results': user_data
            })
        else:
            user_query = models.User.objects.filter(is_delete=False).all()

            serializer_instance = serializers.UserSerializer(user_query, many=True)
            user_list_data = serializer_instance.data

            return Response({
                'status': 0,
                'msg': 'ok',
                'results': user_list_data
            })

##### 시리얼라이저 클래스

"""
1) 시리얼라이징 필드 설정: 모델 클래스 속성명과 유형 일치 필요 (조건부 설정 불필요)
2) 모델에 존재하지만 시리얼라이저에 없는 필드는 무시
3) 커스텀 필드 정의 (방법 1): SerializerMethodField() 사용, 값은 get_필드명 메서드 제공
   일반적으로 모델 객체(model_obj)와 관련된 값 제공
"""
class UserSerializer(serializers.Serializer):
    # 1) 모델 클래스 속성명과 유형 일치
    # 2) 제공되지 않은 필드는 시리얼라이징에서 배제
    # 3) 커스텀 필드 정의 시 get_필드명 메서드로 값 제공
    username = serializers.CharField()
    # sex = serializers.IntegerField()

    # gender = serializers.SerializerMethodField()  # 권장하지 않음
    gender = serializers.SerializerMethodField()

    def get_gender(self, obj):
        return obj.get_sex_display()

    # 주의: 고급 시리얼라이저와 뷰에서 자동 처리되는 이미지 등 하위 자원 처리
    icon = serializers.SerializerMethodField()

    def get_icon(self, obj):
        return '%s%s%s' % (settings.BASE_URL, settings.MEDIA_URL, obj.img)

Serializer 역직렬화(기초 지식) ------------------ ##### 뷰 클래스

"""
# 뷰 클래스 역직렬화 과정
# 1) 요청 객체에서 클라이언트 입력 데이터 추출
# 2) 시리얼라이저를 통해 데이터 검증 수행
# 3) 시리얼라이저를 통해 데이터 저장 처리
# 4) 처리 결과 클라이언트에 반환
"""
class UserV2APIView(APIView):
    # 단일 생성
    def post(self, request, *args, **kwargs):
        request_data = request.data
        serializer_instance = serializers.UserDeSerializer(data=request_data)
        if serializer_instance.is_valid():
            # 데이터 저장
            user_obj = serializer_instance.save()
            return Response({
                'status': 0,
                'msg': 'ok',
                'results': serializers.UserSerializer(user_obj).data  # 저장된 객체 다시 시리얼라이징
            })
        else:
            return Response({
                'status': 1,
                'msg': serializer_instance.errors,
            })


##### 시리얼라이저 클래스

"""
1) 시스템 검증 필드와 커스텀 검증 필드 정의 방식 동일: 필드 = 필드유형(조건)
2) 커스텀 검증 필드는 직접 저장 불가, 저장 규칙 설정 또는 필드 제거 필요
3) 모든 필드에 대해 로컬 해커 실행 가능, validate_필드명(self, 값) 메서드
   성공 시 값 직접 반환, 실패 시 ValidationError 발생
4) 시리얼라이저 클래스에 글로벌 해커 존재, validate(self, attrs) 메서드
   성공 시 attrs 반환, 실패 시 ValidationError 발생
5) create 메서드 재정의하여 데이터 저장 처리, 저장된 객체 반환
6) update 메서드 재정의하여 수정 데이터 저장 처리, 저장된 객체 반환
"""
class UserDeSerializer(serializers.Serializer):
    # 시스템 검증 필드
    username = serializers.CharField(min_length=3, max_length=16, error_messages={
        'min_length': '짧음',
        'max_length': '긴 것'
    })
    password = serializers.CharField(min_length=3, max_length=16)

    # required=False 설정 시 필드 제외, 기본값 사용 가능
    sex = serializers.BooleanField(required=False)

    # 커스텀 검증 필드: 설정 문법과 시스템 필드 동일, 저장 불가
    re_password = serializers.CharField(min_length=3, max_length=16)

    # 로컬 해커
    def validate_username(self, value):
        if 'g' in value.lower():
            raise serializers.ValidationError('이름에 g 포함 불가')
        return value

    # 글로벌 해커
    def validate(self, attrs):
        password = attrs.get('password')
        re_password = attrs.pop('re_password')
        if password != re_password:
            raise serializers.ValidationError({'re_password': '비밀번호 불일치'})
        return attrs

    # save() 메서드 호출 시 create/update 실행
    def create(self, validated_data):
        return models.User.objects.create(**validated_data)

    def update(self, instance: models.User, validated_data):
        validated_data.pop('username')
        models.User.objects.filter(pk=instance.id).update(**validated_data)
        return instance

ModelSerializer 시리얼라이징 및 역직렬화(중점) --------------------------- ##### 뷰 클래스

class UserV3APIView(APIView):
    # 단일 조회 / 다중 조회
    def get(self, request, *args, **kwargs):
        pk = kwargs.get('pk')
        if pk:
            user_obj = models.User.objects.filter(is_delete=False, pk=pk).first()
            if not user_obj:
                return Response({
                    'status': 1,
                    'msg': 'pk error',
                }, status=400)

            serializer_instance = serializers.UserModelSerializer(user_obj, many=False)
            return Response({
                'status': 0,
                'msg': 'ok',
                'results': serializer_instance.data
            })
        else:
            user_query = models.User.objects.filter(is_delete=False).all()

            serializer_instance = serializers.UserModelSerializer(user_query, many=True)

            return Response({
                'status': 0,
                'msg': 'ok',
                'results': serializer_instance.data
            })

    # 단일 생성
    def post(self, request, *args, **kwargs):
        serializer_instance = serializers.UserModelSerializer(data=request.data)
        if serializer_instance.is_valid():
            # 데이터 저장
            user_obj = serializer_instance.save()
            return Response({
                'status': 0,
                'msg': 'ok',
                'results': serializers.UserModelSerializer(user_obj).data
            })
        else:
            return Response({
                'status': 1,
                'msg': serializer_instance.errors,
            })

##### 시리얼라이저 클래스

""" ModelSerializer 시리얼라이징 및 역직렬화 요약
1) ModelSerializer 상속 시 Meta 클래스에서 설정
2) model 설정: 시리얼라이징 관련 모델 테이블 연결
3) fields 설정: 참여 필드의 플러그인식 설정
4) extra_kwargs 설정:
   시스템 필드 분류: 읽기 전용(read_only), 쓰기 전용(write_only), 읽기/쓰기
   필드 필수 여부: required
   선택 필드: extra_kwargs에서 설정, default 값 제공
5) 커스텀 시리얼라이징 필드:
   첫 번째(비추천): SerializerMethodField() 사용
   두 번째(권장): 모델 클래스에서 @property 사용, 플러그인식 지원
6) 커스텀 역직렬화 필드:
   Serializer와 동일 규칙, extra_kwargs 설정 무효
   시스템 필드와 동일 설정이므로 write_only 설정 필수
7) 로컬/글로벌 해커는 Serializer와 동일
8) create/update 메서드 재정의 필요 없음
"""
class UserModelSerializer(serializers.ModelSerializer):
    # 첫 번째 커스텀 시리얼라이징 필드: fields에 명시 필요
    # gender = serializers.SerializerMethodField()
    # def get_gender(self, obj):
    #     return obj.get_sex_display()

    # 커스텀 역직렬화 필드 정의: Serializer와 동일 규칙, extra_kwargs 설정 무효
    # 주의: 시스템 필드와 동일 설정이므로 write_only 설정 필수
    confirm_password = serializers.CharField(min_length=3, max_length=16, write_only=True)

    class Meta:
        model = models.User
        # fields: 참여 필드의 플러그인식 설정
        fields = ('username', 'gender', 'icon', 'password', 'sex', 'confirm_password')
        extra_kwargs = {
            'username': {
                'min_length': 3,
                'max_length': 10,
                'error_messages': {
                    'min_length': '짧음',
                    'max_length': '긴 것'
                }
            },
            'gender': {
                'read_only': True,  # 커스텀 시리얼라이징 필드는 기본 read_only
            },
            'password': {
                'write_only': True,
            },
            'sex': {
                'write_only': True,
                # 'required': True  # 기본값이 있는 필드는 선택 필드
            }
        }

    # 로컬/글로벌 해커는 Meta와 동일 레벨
    def validate_username(self, value):
        if 'g' in value.lower():
            raise serializers.ValidationError('이름에 g 포함 불가')
        return value

    def validate(self, attrs):
        password = attrs.get('password')
        confirm_password = attrs.pop('confirm_password')
        if password != confirm_password:
            raise serializers.ValidationError({'confirm_password': '비밀번호 불일치'})
        return attrs

    # create/update 메서드 재정의 필요 없음, ModelSerializer가 제공
##### 모델 클래스

from django.db import models

class User(models.Model):
    SEX_CHOICES = (
        (0, '여'),
        (1, '남'),
    )

    username = models.CharField(max_length=64, verbose_name='사용자명', blank=True, unique=True)
    password = models.CharField(max_length=64, verbose_name='암호')
    sex = models.IntegerField(choices=SEX_CHOICES, default=0, verbose_name='성별')
    img = models.ImageField(upload_to='img', default='img/default.png', verbose_name='프로필 사진')
    # 실제 삭제 대신 is_delete 필드 사용
    is_delete = models.BooleanField(default=False, verbose_name='삭제 여부')
    created_time = models.DateTimeField(auto_now_add=True, verbose_name='생성 시간')

    # 두 번째 커스텀 시리얼라이징 필드(플러그인식, 권장)
    @property
    def gender(self):
        return self.get_sex_display()

    @property
    def icon(self):
        from django.conf import settings
        return '%s%s%s' % (settings.BASE_URL, settings.MEDIA_URL, self.img)

    class Meta:
        db_table = 'old_boy_user'
        verbose_name_plural = '사용자 테이블'

    def __str__(self):
        return self.username

태그: Django REST Framework Serializer modelserializer API View 데이터 검증

6월 17일 19:26에 게시됨