Study Anything ๐ง
[Django] DRF : Serializer ๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ, ์ ํจ์ฑ ๊ฒ์ฆ ๋ณธ๋ฌธ
์ฐธ๊ณ : https://www.django-rest-framework.org/api-guide/serializers/
์ฅ๊ณ ๋ ์คํธ ํ๋ ์์ํฌ(Django REST Framework)๋ฅผ ์ด์ฉํด REST API ํ๊ฒฝ์ ๊ตฌ์ถํ๋ค๋ณด๋ฉด
Serializer๋ฅผ ๊ฑฐ์ ํ์์ ์ผ๋ก ์ฌ์ฉํ๊ฒ ๋๋ค.
ํ์ง๋ง Serializer๊ฐ ์ด๋ค ๊ฐ๋ ์ธ์ง ์ ์๋ฟ์ง ์์์ Serializer์ ๋ํด ๊ณต๋ถํ๊ณ ์๊ฒ ๋ ๊ฒ๋ค์ ์ ๋ฆฌํด๋ณด๋ ค๊ณ ํ๋ค.
(1) Serializer๋?
์ฟผ๋ฆฌ์ ์ด๋ ๋ชจ๋ธ ์ธ์คํด์ค ๊ฐ์ ๋ณต์กํ ๋ฐ์ดํฐ๋ค์ ๋ค์ดํฐ๋ธ ํ์ด์ฌ ๋ฐ์ดํฐ ํ์ ์ผ๋ก ๋ณํํ์ฌ
json, xml ๋ฑ์ ์ปจํ ์ธ ํ์ ์ผ๋ก ์ฝ๊ฒ ๋ ๋๋งํ ์ ์๊ฒ ํ๋ค.
๋ฐ์ดํฐ๋ฅผ ์ง๋ ฌํํ๋ Serializer๋ ์ญ์ง๋ ฌํ ๊ธฐ๋ฅ ๋ํ ์ ๊ณตํ๊ธฐ ๋๋ฌธ์
๋ค์ด์ค๋ ๋ฐ์ดํฐ๋ฅผ ๊ฒ์ฆํ ํ์ ๋ถ์๋ ๋ฐ์ดํฐ๋ฅผ ๋ณต์กํ ํ์์ผ๋ก ๋ค์ ๋ณํํ ์๋ ์๋ค.
REST Framework์ Serializer๋ ์ฅ๊ณ ์ Form, ModelForm ํด๋์ค์ ๋งค์ฐ ์ ์ฌํ๊ฒ ์๋ํ๋ค.
DRF๊ฐ ์ ๊ณตํ๋ Serializer ํด๋์ค๋ ์๋ต์ ์ถ๋ ฅ์ ํธ๋ฆฌํ๊ฒ ๊ด๋ฆฌํ ์ ์๊ณ ,
ModelSerializer๋ ๋ชจ๋ธ ์ธ์คํด์ค ๋ฐ ์ฟผ๋ฆฌ ์งํฉ์ ์ฒ๋ฆฌํ๋ ์ง๋ ฌํ ํ๋ก๊ทธ๋จ์ ๊ฐํธํ๊ฒ ๋ง๋ค ์ ์๋๋ก ํ๋ค.
(2) Serializer ์ ์ธํ๊ณ ๋ชจ๋ธ ์ง๋ ฌํ/์ญ์ง๋ ฌํ ํ๊ธฐ
๋ค์๊ณผ ๊ฐ์ ๋ชจ๋ธ์ ์ ์ธํ๋ค๊ณ ๊ฐ์ ํ์.
(์์๋ก ์ฌ์ฉํ๋ ๋ชจ๋ ์ฝ๋์ ์ถ์ฒ๋ ๊ณต์ ์ฌ์ดํธ์ด๋ค.)
# models.py
from datetime import datetime
class Comment:
def __init__(self, email, content, created=None):
self.email = email
self.content = content
self.created = created or datetime.now()
comment = Comment(email='leila@example.com', content='foo bar')
๋ค์์ผ๋ก๋ ํด๋น ๋ชจ๋ธ์ ์ง๋ ฌํํด์ ์ฌ์ฉํ ์ ์๋๋ก Serializer๋ฅผ ์ ์ธํ๋ค.
# serializer.py
from rest_framework import serializers
class CommentSerializer(serializers.Serializer):
email = serializers.EmailField()
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
์ด์ CommentSerializer(์ดํ CS)๋ฅผ ์ฌ์ฉํ์ฌ Comment ๊ฐ์ฒด์ Comment ๋ฆฌ์คํธ๋ฅผ ์ง๋ ฌํํ ์ ์๋ค.
serializer = CommentSerializer(comment)
serializer.data
# ๋ฐ์ดํฐ๊ฐ ๋์
๋๋ฆฌ ํ์
์ผ๋ก ๋ฐํ๋๋ค.
๋ค์ ์ฝ๋๋ฅผ ์์ฑํด ๋ชจ๋ธ ์ธ์คํด์ค๋ฅผ ํ์ด์ฌ ๋ค์ดํฐ๋ธ ๋ฐ์ดํฐ ์ ํ์ผ๋ก ๋ณํํ๋ค.
์ง๋ ฌํ ํ๋ก์ธ์ค๋ฅผ ๋ง๋ฌด๋ฆฌํ๊ธฐ ์ํด์ ๋ฐ์ดํฐ๋ฅผ json์ผ๋ก ๋ ๋๋งํ๋ค.
from rest_framework.renderers import JSONRenderer
json = JSONRenderer().render(serializer.data)
json
# b'{"email":"leila@example.com","content":"foo bar","created":"2016-01-27T15:17:10.375877"}'
์ญ์ง๋ ฌํ๋ ๋น์ทํ๊ฒ ๊ตฌํํ ์ ์๋ค. ๋จผ์ , stream์ ํ์ด์ฌ ๋ค์ดํฐ๋ธ ๋ฐ์ดํฐ ํ์
์ผ๋ก ๊ตฌ๋ฌธ ๋ถ์ํ๋ค.
import io
from rest_framework.parsers import JSONParser
stream = io.BytesIO(json)
data = JSONParser().parse(stream)
๋ค์์ผ๋ก, ์ด๋ฐ ๊ธฐ๋ณธ ๋ฐ์ดํฐ ํ์ ์ ๊ฒ์ฆ๋ ๋ฐ์ดํฐ ๋์ ๋๋ฆฌ๋ก ๋ณํํ๋ค.
serializer = CommentSerializer(data=data)
serializer.is_valid()
# True
serializer.validated_data
# ๋ฐ์ดํฐ๊ฐ ๋์
๋๋ฆฌ ํ์
์ผ๋ก ๋ฐํ๋จ
(3) ์ธ์คํด์ค ์ ์ฅํ๊ธฐ
๊ฒ์ฆ๋ ๋ฐ์ดํฐ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์์ ํ ๊ฐ์ฒด ์ธ์คํด์ค๋ฅผ ๋ฐํํ๋ ค๋ฉด
create() ๋ฐ update() ๋ฉ์๋๋ฅผ ํ๋ ๋๋ ๋ ๋ค ๊ตฌํํด์ผ ํ๋ค.
# serializer.py
class CommentSerializer(serializers.Serializer):
email = serializers.EmailField()
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
def create(self, validated_data):
return Comment(**validated_data)
def update(self, instance, validated_data):
instance.email = validated_data.get('email', instance.email)
instance.content = validated_data.get('content', instance.content)
instance.created = validated_data.get('created', instance.created)
return instance
๊ฐ์ฒด ์ธ์คํด์ค๊ฐ Django ๋ชจ๋ธ๊ณผ ์ผ์นํ ๊ฒฝ์ฐ, ๊ตฌํํ ๋ฉ์๋๊ฐ ๊ฐ์ฒด๋ฅผ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅํ๊ฒ ํ ์๋ ์๋ค.
# serializer.py
def create(self, validated_data):
return Comment.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.email = validated_data.get('email', instance.email)
instance.content = validated_data.get('content', instance.content)
instance.created = validated_data.get('created', instance.created)
instance.save()
return instance
์ด ๊ณผ์ ์ ๊ฑฐ์น๋ฉด ๋ฐ์ดํฐ๋ฅผ ์ญ์ง๋ ฌํํ ๋ save() ๋ฉ์๋๋ฅผ ํธ์ถํ์ฌ ๊ฐ์ฒด ์ธ์คํด์ค๋ฅผ ๋ฐํํ ์ ์์ต๋๋ค.
(๋จ, ๋ฐ์ดํฐ๊ฐ ๊ฒ์ฆ๋ ์ํ์ผ ๋)
save() ๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉด Serializer ํด๋์ค๋ฅผ ์ธ์คํด์คํ ํ ๋ ๊ธฐ์กด ์ธ์คํด์ค๊ฐ ์ ๋ฌ๋ ๊ฒ์ธ์ง์ ์ฌ๋ถ์ ๋ฐ๋ผ
์ ์ธ์คํด์ค๊ฐ ์์ฑ๋๊ฑฐ๋ ๊ธฐ์กด ์ธ์คํด์ค๊ฐ ์ ๋ฐ์ดํธ๋๋ค.
comment = serializer.save()
# ๊ธฐ์กด์ ์๋ ์ธ์คํด์ค์ผ ๋: save() will create a new instance.
serializer = CommentSerializer(data=data)
# ๊ธฐ์กด์ ์๋ ์ธ์คํด์ค์ผ ๋: save() will update the existing `comment` instance.
serializer = CommentSerializer(comment, data=data)
๋ค๋ง create()์ update() ๋ฉ์๋๋ ์ ํ์ฌํญ์ผ๋ก, Serializer ํด๋์ค๋ฅผ ์ด๋ป๊ฒ ์ฌ์ฉํ ๊ฒ์ธ์ง์ ๋ฐ๋ผ์
ํ๋ ๋๋ ๋ ๋ค ๊ตฌํํ ์๋, ๊ตฌํํ์ง ์์ ์๋ ์๋ค.
save() ๋ฉ์๋์ ์ถ๊ฐ ํน์ฑ์ ์ ๋ฌํ ์๋ ์๋๋ฐ, ๊ฒฝ์ฐ์ ๋ฐ๋ผ ์ธ์คํด์ค๋ฅผ ์ ์ฅํ ๋ ์ถ๊ฐ ๋ฐ์ดํฐ๋ฅผ ์ฃผ์
ํ ์ ์๋ค.
์๋ฅผ ๋ค์ด ์ด ์ถ๊ฐํ๋ ๋ฐ์ดํฐ์๋ ํ์ฌ ์ฌ์ฉ์, ํ์ฌ ์๊ฐ ๋๋ ์์ฒญ ๋ฐ์ดํฐ๊ฐ ์๋ ๋ค๋ฅธ ์ ๋ณด๊ฐ ํฌํจ๋ ์ ์๋ค.
์ฌ์ฉ ๋ฐฉ๋ฒ์ ๋ค์ ์ฝ๋์ฒ๋ผ save() ๋ฉ์๋๋ฅผ ํธ์ถํ ๋ ์ถ๊ฐ ํค์๋ ์ธ์๋ฅผ ํฌํจํ๋ฉด ๋๋ค.
์ถ๊ฐํ ์ธ์๋ create() ๋๋ update() ๋ฉ์๋๊ฐ ํธ์ถ๋ ๋ validated_data ์ธ์์ ํฌํจ๋๋ค.
(์์ ์์ฑํ create, update ๋ฉ์๋ ์ฐธ๊ณ )
serializer.save(owner=request.user)
๊ตฌํํ๋ ค๋ ๊ธฐ๋ฅ์ ๊ฒฝ์ฐ์ ๋ฐ๋ผ ์ ์ธ์คํด์ค๋ฅผ ๋ง๋ค์ง ์๊ณ ๋ฉ์ผ์ด๋ ๋ค๋ฅธ ๋ฉ์ธ์ง๋ฅผ ๋ณด๋ด๋ ๋ฑ
create()๋ update() ๋ฉ์๋์ ์ด๋ฆ์ด ์๋ฏธ๊ฐ ์์ ์ ์๋ค.
๊ทธ๋ด ๋ save() ๋ฉ์๋๋ฅผ ์ง์ ์ค๋ฒ๋ผ์ด๋ฉ ํ ์๋ ์๋ค.
์ด๋ฐ ๊ฒฝ์ฐ์๋ Serializer์ validated_data ์์ฑ์ ์ง์ ์ก์ธ์คํด์ผ ํ๋ค.
class ContactForm(serializers.Serializer):
email = serializers.EmailField()
message = serializers.CharField()
def save(self):
email = self.validated_data['email']
message = self.validated_data['message']
send_email(from=email, message=message)
(4) ์ ํจ์ฑ ๊ฒ์ฆํ๊ธฐ (Validation)
๋ฐ์ดํฐ๋ฅผ ์ญ์ง๋ ฌํ ํ ๋, ๊ฒ์ฆ๋ ๋ฐ์ดํฐ์ ์ก์ธ์คํ๊ฑฐ๋ ์ธ์คํด์ค๋ฅผ ์ ์ฅํ๊ธฐ ์ ์๋
ํญ์ is_valid() ๋ฉ์๋๋ฅผ ํธ์ถํด์ผ ํ๋ค.
๋ง์ฝ ๊ฒ์ฆ ๊ณผ์ ์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด error ์์ฑ์ ์๋ฌ ๋ฉ์ธ์ง๋ฅผ ๋ํ๋ด๋ ๋์
๋๋ฆฌ๊ฐ ํฌํจ๋๋ค. (์ ์ฅ๋๋ค.)
๋์
๋๋ฆฌ์ ๊ฐ ํค๋ ํ๋ ์ด๋ฆ์ด ๋๊ณ , ๊ทธ ๊ฐ์ ํด๋นํ๋ ํ๋์ ํด๋นํ๋ ์ค๋ฅ ๋ฉ์ธ์ง๊ฐ ๋๋ค.
non_field_errors ํค๋ ์์ ์ ์๊ณ , ์ด ๊ฒฝ์ฐ๋ ์ผ๋ฐ์ ์ธ ๊ฒ์ฆ ์ค๋ฅ ๋ชฉ๋ก์ด ๋๋ค.
๊ฐ์ฒด ๋ฆฌ์คํธ๋ฅผ ์ญ์ง๋ ฌํ ํ ๋, ์ค๋ฅ๋ ๊ฐ ํญ๋ชฉ์ ๋ํ๋ด๋ ๋์
๋๋ฆฌ ๋ชฉ๋ก์ผ๋ก ๋ฐํ๋๋ค.
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.']}
๋ํ is_valid() ๋ฉ์๋๋ raise_exception ํ๋๊ทธ๋ฅผ ์ฌ์ฉํ์ฌ ๊ฒ์ฆ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ ๋
serializers.ValidationError ์์ธ๋ฅผ ๋ฐ์์ํจ๋ค.
์ด๋ฐ ์์ธ๋ค์ HTTP 400 ๋ฐ์์ ๋ฐํํ๋ REST ํ๋ ์์ํฌ ์ ๊ณต์ ๊ธฐ๋ณธ ์์ธ ํธ๋ค๋ฌ๋ฅผ ํตํด ์๋์ผ๋ก ์ฒ๋ฆฌ๋๋ค.
# ๋ฐ์ดํฐ๊ฐ ๊ฒ์ฆ๋์ง ์์ผ๋ฉด 400 ์๋ต์ ๋ฐํํ๋ค.
serializer.is_valid(raise_exception=True)
Serializer ํ์ ํด๋์ค์ validate_<field_name>() ๋ฉ์๋๋ฅผ ์ถ๊ฐํด์
์ฌ์ฉ์ ์ง์ ์ ํ๋ ์์ค ๊ฒ์ฆ์ ์ง์ ํ ์ ์๋ค.
์ด๋ ์ฅ๊ณ Form์ clean_[field_name]() ๋ฉ์๋์๋ ๋น์ทํ๋ฉฐ,
๊ฒ์ฆ์ด ํ์ํ ํ๋ ๊ฐ๋ฅผ ์ธ์๋ก ํ๋ ๋จ์ผ ์ธ์๋ฅผ ์ฌ์ฉํ๋ค.
์ด ๋ฉ์๋๋ ๊ฒ์ฆ๋ ๊ฐ์ ๋ฐํํ๊ฑฐ๋ serializers.ValidationError๋ฅผ ๋ฐ์์์ผ์ผ ํ๋ค.
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
์ฌ๋ฌ ํ๋์ ์ก์ธ์คํด์ผ ํ๋ ๋ค๋ฅธ ๊ฐ์ฒด ์์ค์ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ์ํํ๊ธฐ ์ํด์
Serializer ํ์ ํด๋์ค์ validate() ๋ผ๋ ๋ฉ์๋๋ฅผ ์ถ๊ฐํ๋ค.
์ด ๋ฉ์๋๋ ํ๋ ๊ฐ์ ๋์
๋๋ฆฌ ์ธ์๋ฅผ ๋จ์ผ๋ก ์ฌ์ฉํ๋ฉฐ,
ํ๋ ์์ค๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก ๊ฒ์ฆ๋ ๊ฐ์ ๋ฐํํ๊ฑฐ๋ ํ์ํ ๊ฒฝ์ฐ์ serializers.ValidationError๋ฅผ ๋ฐ์์์ผ์ผ ํ๋ค.
from rest_framework import serializers
class EventSerializer(serializers.Serializer):
description = serializers.CharField(max_length=100)
start = serializers.DateTimeField()
finish = serializers.DateTimeField()
def validate(self, data):
"""
Check that start is before finish.
"""
if data['start'] > data['finish']:
raise serializers.ValidationError("finish must occur after start")
return data
Serializer์ ๊ฐ๋ณ ํ๋๋ ํ๋ ์ธ์คํด์ค์ ์ ์ธํ์ฌ ๊ฒ์ฆ์(Validator)๋ฅผ ํฌํจํ ์ ์๋ค.
def multiple_of_ten(value):
if value % 10 != 0:
raise serializers.ValidationError('Not a multiple of ten')
class GameRecord(serializers.Serializer):
score = IntegerField(validators=[multiple_of_ten])
...
Serializer ํด๋์ค์๋ ์ ์ฒด ํ๋ ๋ฐ์ดํฐ ์งํฉ์ ์ ์ฉ๋๋ ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ๊ฒ์ฆ์๊ฐ ํฌํจ๋ ์ ์์ผ๋ฉฐ,
์ด๋ฐ ๊ฒ์ฆ์๋ ๋ค์๊ณผ ๊ฐ์ด ๋ด๋ถ ๋ฉํ ํด๋์ค์ ์ ์ธํจ์ผ๋ก์จ ํฌํจ๋๋ค.
class EventSerializer(serializers.Serializer):
name = serializers.CharField()
room_number = serializers.IntegerField(choices=[101, 102, 103, 201])
date = serializers.DateField()
class Meta:
# Each room only has one event per day.
validators = [
UniqueTogetherValidator(
queryset=Event.objects.all(),
fields=['room_number', 'date']
)
]
์ด๋ฒ ํฌ์คํธ์์๋ Serializer์ ๊ธฐ๋ณธ์ ์ธ ์ฌ์ฉ๋ฒ๊ณผ ์ ํจ์ฑ์ ๊ฒ์ฌํ๋ ๋ฐฉ๋ฒ์ ์์๋ณด์๋ค.
๋ ์์ธํ๊ณ ๋ค์ํ ๋ด์ฉ๋ค์ด ๋ง์ง๋ง, ๊ธฐ๋ณธ์ ์ผ๋ก ํ๋ก์ ํธ์ ์ ์ฉํ ์ ์๋ ๊ฐ๋ ์ ์ด ์ ๋์ธ ๊ฒ ๊ฐ๋ค.
์ค๋ ์คํฐ๋ ํ ๋ด์ฉ๋ค์ ๋ฐํ์ผ๋ก ํ๋ก์ ํธ์์ Serializer๋ฅผ ์ ์ฌ์ฉํด ๋ณผ ์ ์์ ๊ฒ ๊ฐ๋ค.
'์คํฐ๋ > Web' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Django] ํ ํ๋ก์ ํธ ๋ด์์ ๋ค๋ฅธ ์ฑ์ ๋ชจ๋ธ ์ฌ์ฉํ๊ธฐ (0) | 2022.02.09 |
---|---|
[Django] ๋ชจ๋ธ ์์ ํ๊ณ ์ญ์ ํ๊ธฐ (0) | 2022.02.08 |
[Django] DRF : REST API๋ฅผ ํตํด ์ฅ๊ณ ์ ๋ฆฌ์กํธ ์ฐ๊ฒฐํ๊ธฐ (0) | 2022.01.29 |
[Django] DRF : ์ฅ๊ณ ๋ชจ๋ธ์ json ๊ฐ์ฒด๋ก ์ถ์ถํ๊ธฐ (0) | 2022.01.19 |
[Django] ๋ชจ๋ธ ๋ง๋ค๊ณ ํ์ด์ง์์ ์กฐํํ๊ธฐ (0) | 2022.01.13 |