슈코딩

[Django] DRF의 꽃 Serializer 본문

코딩공부/Django

[Django] DRF의 꽃 Serializer

Roshu 2022. 6. 24. 02:11

 

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 validatorvalidator 이후에 동작하고 별개로 동작을 한다.

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
Comments