슈코딩
[Django] DRF의 꽃 Serializer 본문
1. DRF (Django Rest Framework)
🤔 DRF란?
DRF는 Python으로 개발된 대표적인 웹 프레임워크중 하나인 Django 안에서
RESTful API 서버를 쉽게 만들 수 있게 도와주는 라이브러리이다. 특징 중에서 가장 대표적인 것은
Serialize 기능이 존재한다는 것이다. 그렇다면 Serializer는 무엇일까?
#기본설정
pip install django
pip install djangorestframework
# settings.py
INSTALLED_APPS = [
... ,
... ,
... ,
(app_name),
'rest_framework', <
# settings.py
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [ # 기본적인 view 접근 권한 지정
'rest_framework.permissions.AllowAny'
],
'DEFAULT_AUTHENTICATION_CLASSES': [ # session 혹은 token을 인증 할 클래스 설정
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication'
],
'DEFAULT_PARSER_CLASSES': [ # request.data 속성에 액세스 할 때 사용되는 파서 지정
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser'
]
}
2. 😲Serializer란?
DRF의 꽃이라고 불리는 serializer는 직렬화, 즉 Frontend로 데이터를 보내기 위해서 Django 내부의 복잡한 데이터들을
JSON/XML 등의 형태로 쉽게 변환(직렬화)해주는 기능이다.
serializer Meta class
- serializer에서 사용되는 설정 파일이다.
- model에 사용 될 테이블을 적어주고, field에 사용될 필드를 적어준다.
- extra_kwargs, read_only_fields와 같은 옵션을 통해 다양한 설정이 가능하다.
기본 사용 방법
#serializers.py
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
class Meta:
# serializer에 사용될 model, field지정
model = User
# 모든 필드를 사용하고 싶을 경우 fields = "__all__"로 사용
fields = ["username", "password", "fullname", "email"]
먼저 UserSeirializer를 사용할거면, user 앱에 serializers.py라는 파일을 새로 만들어서 코드를 작성한다.
그리고 views.py에 import를 해서 사용한다.
from rest_framework.response import Response
from rest_framework import status
from user.serializers import UserSerializer
def get(self, request):
user = request.user
# serializer에 queryset을 인자로 줄 경우 many=True 옵션을 사용해야 한다.
serialized_user_data = UserSerializer(user).data
return Response(serialized_user_data, status=status.HTTP_200_OK)
# return data
"""
{
"username": "user",
"password": "pbkdf2_sha256$320000$u5YnmKo9luab9csqWpzRsa$pKfqHnBiF5Rgdo1Mj9nxNOdhpAl9AhPVXFPXkbPz7Mg=",
"fullname": "user's name",
"email": "user@email.com"
}
"""
그리고 외래 키 관계에 있는 테이블이 있을 경우에는, 해당 테이블의 serializer를 생성해 함께 사용할 수 있다.
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields = "__all__"
class UserSerializer(serializers.ModelSerializer):
"""
외래 키는 UserProfile에서 User 테이블로 설정되어 있지만
one to one 필드기 때문에 userprofile이라는 명칭으로 역참조가 가능하다.
"""
userprofile = UserProfileSerializer()
class Meta:
model = User
fields = ["username", "password", "fullname", "email", "userprofile"]
SerializerMethodField
SerializerMethodField 를 활용해 원하는 필드를 추가하고, 더 나아가서 여러 serializer들을 함께 사용할 수도 있다.
class ProductSerializer(serializers.ModelSerializer):
reviews = serializers.SerializerMethodField()
average_review = serializers.SerializerMethodField()
def get_reviews(self, obj):
reviews = list(obj.review_set.values())
if len(reviews) == 0:
return "리뷰 없음"
return reviews[-1]
def get_average_review(self, obj):
product_reviews = obj.review_set.values()
rating_list = [review['rating'] for review in product_reviews]
if len(rating_list) == 0:
average_rating = "평점 없음"
else:
average_rating = round(sum(rating_list) / len(rating_list), 1) #round 는 반올림. 뒤에 1은 소숫점 1자리만 보임.2면 2자리
return average_rating
class Meta:
model = ProductModel
fields = "__all__"
Validate
Custom validator는 validator 이후에 동작하고 별개로 동작을 한다.
validator는 데이터의 requierd, invalid 등을 판단하고 Custom validator에서는 사용자가 원하는 validation을 추가로 검증 할 수 있다. ProductSerializer안의 validate함수가 바로 커스텀된 validator다.
def validate(self, data):
http_method = self.context.get("request").method
# context= 를 통해 원하는 데이터를 serializer에 넘겨주고, self.context를 사용해 호출 가능하다.
# serialized_product_data = ProductSerializer(product, context={"some_key": "some_value"}).data
if http_method == "POST":
if data.get("exposure_end") < timezone.now():
raise serializers.ValidationError(
detail={"error": "상품 등록기간이 끝났습니다!"}
)
return data
Create
serializer에서는 validation을 통과할 경우 .save() 메소드를 통해 검증 된 오브젝트를 생성 할 수 있다.
custom creator 코드는 기존 create 코드를 덮어쓰며, custom creator를 생성할 경우 기존 create 코드는 동작하지 않는다.
def create(self, validated_data):
product = ProductModel(**validated_data)
product.save()
product.description += f" <{product.created_date} 에 등록된 상품입니다.>"
product.save()
return product
Update
serializer를 사용해 기존 데이터들 쉽게 수정 할 수 있다.
custom update 코드는 기존 update 코드를 덮어쓰며, custom updater를 생성할 경우 기존 update코드는 동작하지 않는다.
def update(self, instance, validated_data):
# instance에는 입력된 object가 담긴다.
for key, value in validated_data.items():
setattr(instance, key, value)
if instance.updated_at == instance.created_date:
instance.description = f"<{instance.updated_at} 에 수정됨> \n {instance.description}"
instance.save()
else:
split_desc_list = instance.description.split("\\n")
split_desc ="\n".join(split_desc_list[1:])
updated_desc = f"<{instance.updated_at} 에 수정됨>\n{split_desc}"
instance.description = updated_desc
instance.save()
return instance
이렇게 위에서 설명한 기능들을 합치면 하나의 ProductSerializer가 완성이 된다.
#product앱의 serializers.py
class ReviewSerializer(serializers.ModelSerializer):
class Meta:
model = ReviewModel
fields = "__all__"
class ProductSerializer(serializers.ModelSerializer):
reviews = serializers.SerializerMethodField()
average_review = serializers.SerializerMethodField()
review_author = ReviewSerializer(read_only=True, source="review_set", many=True)
is_active = serializers.BooleanField(default=True)
def get_reviews(self, obj):
reviews = list(obj.review_set.values())
if len(reviews) == 0:
return "리뷰 없음"
return reviews[-1]
def get_average_review(self, obj):
product_reviews = obj.review_set.values()
rating_list = [review['rating'] for review in product_reviews]
if len(rating_list) == 0:
average_rating = "평점 없음"
else:
average_rating = round(sum(rating_list) / len(rating_list), 1) #round 는 반올림. 뒤에 1은 소숫점 1자리만 보임.2면 2자리
return average_rating
def validate(self, data):
http_method = self.context.get("request").method
if http_method == "POST":
if data.get("exposure_end") < timezone.now():
raise serializers.ValidationError(
detail={"error": "상품 등록기간이 끝났습니다!"}
)
return data
def create(self, validated_data):
product = ProductModel(**validated_data)
product.save()
product.description += f" <{product.created_date} 에 등록된 상품입니다.>"
product.save()
return product
def update(self, instance, validated_data):
for key, value in validated_data.items():
setattr(instance, key, value)
if instance.updated_at == instance.created_date:
instance.description = f"<{instance.updated_at} 에 수정됨> \n {instance.description}"
instance.save()
else:
split_desc_list = instance.description.split("\\n")
split_desc ="\n".join(split_desc_list[1:])
updated_desc = f"<{instance.updated_at} 에 수정됨>\n{split_desc}"
instance.description = updated_desc
instance.save()
return instance
class Meta:
model = ProductModel
fields = ["seller", "title", "thumbnail", "description",
"price", "average_review","review_author", "reviews", "is_active", "exposure_end", "exposure_start", "created_date", "updated_at"]
3. 😊serializer를 사용해보고 느낀점
serializer를 이번 특강에서 배우고 직접 사용을 해보니 처음에는 익숙한 코드가 아니라서 이해하는게 어려웠지만,
과제를 통해 조금씩 이해도가 높아질수록 다양하게 활용이 가능하다는걸 느끼고, serializer가 아니면 이 복잡한
데이터들을 어떻게 Json으로 frontend에 보낼까를 생각해보니 점점 serializer를 사용하는 이유를 몸소 깨닫는
부분이 생기는 것 같다. 아직은 기본적인 serializer field만 사용해봤지만 점차 이해도가 높아지면 ReadOnlyField,
RegexField, SlugField, BooleanField와 같은 다양한 필드에 대해 공부를 해서 다음 프로젝트때 적용 시켜볼 기회가
생기면 적용 해볼 생각이다.
ps. 이창호 튜터님! 이번에 DRF강의를 정말 열정적으로 매일 새벽까지 준비해주셔서 정말 감사합니다.
'코딩공부 > Django' 카테고리의 다른 글
[Django] DRF 이용한 JWT사용 (0) | 2022.07.14 |
---|---|
[Django] Q() 객체 Query 활용 (0) | 2022.06.28 |
[Django] Admin 페이지 커스텀 (0) | 2022.06.24 |
[Django] DRF 특강 2,3일차 기본 개념 정리 (0) | 2022.06.20 |
[Django] DRF 특강 1일차 (0) | 2022.06.15 |