"""
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