티스토리 뷰

반응형

django를 내부의 로직에 따른 복잡한 데이터를 다른 framework에서도 이해할 수 있도록 변환이 필요할 것이다. 이러한 목적으로 나온 것이 Serializer이다. 따라서 serializer는 fontend와 장고를 연결할 때 보통 처음 접하게 된다. serializer에 대한 개념을 찾아보면, 직렬화라는 이야기가 많이 나오지만, 쉽게 설명하면 frontend로 데이터를 보내기 위해 장고 내부의 복잡한 데이터들을 json/xml 등의 형태로 데이터를 변환해준다고 생각을 하면 된다.

 

 

사용 시기

우리는 장고에서 model.py를 통해 데이터베이스(DB)를 구축한다. 그리고 serializer는 DB를 json으로 바꿀 때 사용되며, 반대로 json을 DB로 바꿀 때는 deserializer가 사용된다. serializer 시에는 각각의 DB의 칼럼들로 정의해 줘야 한다.

 

 

 

step1 기초 흐름

DB 생성

models.py에서 DB 설정을 간단히 만들어 보자

 

from django.db import models

# Create your models here.
class Article(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()

 

Article이라는 모델에 columns가 title과 content가 포함되어 있다. 그리고 article내부에 DB가 저장되어 있다고 가정하겠다.  생성 ex_ article = Article(title="hellow world", content="big world") => article.save()

 

 

 

Serializer 생성

serialize와 deserialize를 하기 위해서는 아래와 같은 serializer 선언이 필요하다. 보통 serializers.py라는 파일을 만들어 아래와 같이 작성한다.

 

from rest_framework import serializers

class ArticleSerializer(serializers.Serializer):
    title = serializers.CharField()
    content = serializers.TextField()

 

 

 

 

Serializing 사용법

위에서 만든 serializer에 모델을 넣어준다.

serializer = ArticleSerializer(Article)
print(serializer)
'''
{ 'title': 'hellow world', 'content': 'big world' }
'''

 

결괏값을 보면, 위와 같이 장고의 DB의 내용이 딕셔너리 형태로 바뀐다. 그러나 아직 JSON형태는 아니다. 아래의 과정을 통해 JSON으로 만들어보자.

 

 

from rest_framework.renderers import JSONRenderer

json = JSONRenderer().render(serializer.data)
json
'''
b'{ "title": "hellow world", "content": "big world" }
'''

 

 

 

 

Deserializing 사용법

위에서 만든 json을 장고에서 활용하기 위한 딕셔너리 형태로 다시 바꾸는 방법은 아래와 같다.

 

import io
from rest_framework.parsers import JSONParser

stream = io.BytesIO(json)
data = JSONParser().parse(stream)

serializer = ArticleSerializer(data=data)
serializer.is_valid()
# True
serializer.validated_data
'''
{ 'title': 'hellow world', 'content': 'big world' }
'''

 

 

 

Serializer로 DB 생성, 수정하기

serializer는 create와 update 메서드를 추가해서 기능 추가가 가능하다. 우선 아래의 예를 보자.

 

 

from rest_framework import serializers

class ArticleSerializer(serializers.Serializer):
    title = serializers.CharField()
    content = serializers.TextField()
    def create(self, validated_data):
        return Article.objects.create(**validated_data)

    def update(self, instance, validated_data):
        instance.title = validated_data.get('title', instance.title)
        instance.content = validated_data.get('content', instance.content)
        instance.save()
        return instance

 

create() 메서드

> save() 메서드를 호출함으로 DB 인스턴스를 생성하고자 할 때 정의한다.

 

update() 메서드

> saver() 메서드를 호출함으로 DB 인스턴스를 수정하고자 할 때 정의한다.

 

 

정리하면, 우리는 serializer. save()를 통해 호출할 때 인스턴스의 생성이나 수정이 이루어지는데, 어떻게 로직이 이루어지는지를 정하기 위해 create()와 update()를 사용한다. 그렇다면 .save활용하는 방법에 대해 알아보자.

 

 

 

 

.save() 사용법

위에서 모델, serializer를 다 만들어 놨다. serializer.save()를 사용하려면 serializer 변수 설정을 먼저 해야 한다. 설정 방법은 2가지다.

 

serializer = ArticleSerializer(data=data)

인자가 하나밖에 없기 때문에 ArticleSerializer 클래스의 create로직이 사용된다.

create 메서드에 의해 DB에 생성이 된다.

 

 

serializer = ArticleSerializer(article, data=data)

인자가 둘이기 때문에 ArticleSerializer 클래스의 update로직이 사용된다.

article은 models.py에서 정의한 Article의 인스턴트이다.

update로직에 의해 DB 값들이 수정된다.

 

 

serializer를 만들었으니 serializer.save()를 사용해서 실행하면 된다. 이때 아래와 같이 save에 인자를 추가해서 validated_data의 인수로 포함시키기도 가능하다.

 

serializer.save(owner=request.user)

 

 

 

 

is_valid()

위에서 우리는 is_valid()를 deserializer에서 사용을 한 적이 있다. deserializer을 하여 데이터에 접근하거나 DB에 저장할 때는 반드시 is_valid()를 호출해서 확인을 해줘야 한다. 우선 공식문서의 예를 가져와서 설명해 보겠다.

 

serializer = CommentSerializer(data={'email': 'foobar', 'content': 'baz'})
serializer.is_valid()
# False
serializer.errors
# {'email': ['Enter a valid e-mail address.'], 'created': ['This field is required.']}

 

CommentSerializer에 데이터를 넣어서 serializer변수에 넣었다. 그리고 is_valid()를 했는데 False라고 나온 것을 확인할 수 있다. is_valid()가 False인 경우에 error속성을 사용 가능하다. 따라서 serializer.errors 통해 valid 검증이 False인 이유를 알 수 있다. 검증 실패 이유는 email의 형식이 잘못되었고, created는 필수 필드인데 없다는 것을 알 수 있다.

 

raise_exception=True를 인자로 아래와 같이 추가하는 것도 가능하다.

 

serializer.is_valid(raise_exception=True)

 

raise_exception=True을 추가하면 , is_valid의 값이 False라면 400 response를 반환해준다.

 

 

 

 

 

. validate_<field_name>

아래와 같이 serializer 코드에 모델에서 지정한 title을 넣어서 subclass로 def validate_title를 넣는 것이 가능하다.

 

from rest_framework import serializers

class BlogPostSerializer(serializers.Serializer):
    title = serializers.CharField(max_length=100)
    content = serializers.CharField()

    def validate_title(self, value):
        """
        Check that the blog post is about Django.
        """
        if 'django' not in value.lower():
            raise serializers.ValidationError("Blog post is not about Django")
        return value

 

장고 froms에서는 .clean_<field_name>과 비슷한 방식으로 유효성 검증이 필요한 부분에 대해서 넣을 수 있다. 조건에 대한 유효성이 통과되면, value값을 그대로 return 하고 통과하지 못하면, serializers.ValidationError를 발생시킨다.

 

추가적으로 <field_name>에 required=False가 serializer로 선언되지 않으면, validation 단계가 진행되지 않는다.

 

 

 

required=False

required=False에 대한 설명이 부족하다 판단되어 아래와 같은 내용을 통해 추가 설명하려고 한다. 코드를 보자.

 

user = UserSerializer(required=False)

 

모든 칼럼을 다 넣어야 하지만, required=False를 넣으면 일부 칼럼에 None이 포함되더라도 error가 발생하지 않는다.

 

 

기본적으로 serializer는 모든 필수 필드의 값을 전달해야 합니다. 그렇지 않으면 유효성 검사 오류가 발생합니다. 그러나 수정 상황에서는 무든 필수 필드를 넣을 필요 없이 수정할 필드만 넣어주면 된다. 그때 사용하는 것이 partial=True이다. 아래와 같이 사용하면 된다.

 

# Update `comment` with partial data
serializer = CommentSerializer(comment, data={'content': 'foo bar'}, partial=True)

 

 

 

nested objects(중첩 객체 다루기)

 serializer 클래스를 만들다 보면 중첩된 serializer를 만드는 경우가 있다. 그 예는 아래와 같다.

 

class UserSerializer(serializers.Serializer):
    email = serializers.EmailField()
    username = serializers.CharField(max_length=100)
    
class CommentSerializer(serializers.Serializer):
    user = UserSerializer(required=False)  # May be an anonymous user.
    edits = EditItemSerializer(many=True)  # A nested list of 'edit' items.
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()

 

위의 예를 보면 CommentSerializer 내부에 UserSerializer가 포함된 user 변수를 만드는 경우를 볼 수 있다. 위의 로직을 보면, comment를 사용하는 User가 로그인을 안 했을 수도 있기 때문에 required=False를 적어서 비로그 인자도 접근을 가능하게 만든 것이라고 보면 된다.

 

 

edits 변수를 지정하는 것을 보면, EditItemSerializer라는 class를 중첩했고, many=True를 사용한 것을 볼 수 있다. many=True를 이해를 위해 아래의 예를 참고하자.

 

queryset = Book.objects.all()
serializer = BookSerializer(queryset, many=True)
serializer.data
# [
#     {'id': 0, 'title': 'The electric kool-aid acid test', 'author': 'Tom Wolfe'},
#     {'id': 1, 'title': 'If this is a man', 'author': 'Primo Levi'},
#     {'id': 2, 'title': 'The wind-up bird chronicle', 'author': 'Haruki Murakami'}
# ]

 

기본적으로 BookSerializer의 인자로 queryset과 many=True가 들어가 있는 것을 알 수 있다. 그러나 보통 serializer 모델에 대입하는 인자는 하나의 객체 인스턴스여야 한다. 따라서 queryset이나, 객체 리스트를 넣으면 에러가 발생한다. 이때, 쿼리 셋이나 객체로 생성된 리스트를 하용하는 인자가 many=True이다. 따라서 코드 작성을 하다 오류가 발생하면, 반드시 many=True를 삽입해야 하는지 생각을 해봐야 한다.

 

 

 

여기까지가 기초라고 생각이 든다. 추가적인 내용을 추후 적겠다.

 

 

-참고-

ModelSerializer

어렵게 생각할 필요 없이 Serializer를 좀 더 간단하게 할 수 있게 도와주는 것이라 생각하자. 정의할 모델의 필드명만 명시해주면 그것들의 타입에 맞게 시리얼라이저의 필드들이 자동으로 정의가 된다. 그리고 create()와 update()도 기본적으로 포함되어 있기 때문에 정의할 필요 없다. (추가 기능을 넣으려면 오버 라이딩해서 사용하면 된다. )

반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함