serializers ๋ queryset๊ณผ ๋ชจ๋ธ ์ธ์คํด์ค์ ๊ฐ์ ๋ณต์กํ ๋ฐ์ดํฐ๋ฅผ JSON
, XML
๋๋ ๋ค๋ฅธ ์ปจํ
์ธ ์ ํ์ผ๋ก ์ฝ๊ฒ ๋ ๋๋งํ ์ ์๋ python ๊ธฐ๋ณธ ๋ฐ์ดํฐ ์ ํ์ผ๋ก ๋ณํ ํด์ค๋ค. ๋ํ deserialization ์ ์ ๊ณตํ์ฌ, ๋ค์ด์ค๋ ๋ฐ์ดํฐ์ ์ ํจ์ฑ์ ์ฒ์ ํ์ธํ ํ์ ๊ตฌ๋ฌธ ๋ถ์์ด ๋ ๋ฐ์ดํฐ๋ฅผ ๋ณตํฉ ํ์์ผ๋ก ๋ค์ ๋ณํํ ์ ์๋ค.
REST ํ๋ ์์ํฌ์ serializers ๋ Django์ Form
, modelForm
ํด๋์ค์ ๋งค์ฐ ์ ์ฌํ๊ฒ ์๋ํ๋ค. serializers ๋ ModelSerializer
, Serializer
ํด๋์ค๋ฅผ ์ ๊ณตํ๋ค.
์ฌ์ฉ์ ์ djangorestframework
๊ฐ ์ค์น๋์ด์๋์ง ํ์ธํด์ผํ๋ค.
Copy $ pip list
Package Version
------------------- -------
Django 2.1.7
djangorestframework 3.9.2
pip 19.0.3
pytz 2018.9
setuptools 40.6.2
Serialzers ์ ์ธ
๊ฐ๋จํ ์๋ฅผ๋ค๊ธฐ ์ํด์ ๊ฐ์ฒด ํ๊ฐ๋ฅผ ์์ฑํ๋ค.
Copy # models.py
from django . db import models
from datetime import datetime
# Create your models here.
class Comment ( models . Model ):
email = models . CharField (max_length = 100 ,blank = False )
content = models . TextField ()
created_at = models . DateTimeField (default = datetime.now)
Copy >>> from quickstart . models import Comment
>>> from datetime import datetime
>>> c = Comment (email = "test@test.com" , content = "testsetstsetset" )
>>> c . save ()
>>> Comment . objects . all ()
< QuerySet [ < Comment : Comment object ( 1 ) > , < Comment : Comment object ( 2 ) > ] >
>>>
comment
๊ฐ์ฒด์ ํด๋นํ๋ ๋ฐ์ดํฐ๋ฅผ serializer, deserializerํ ํ ์ ์๋ serializer๋ฅผ ์ ์ธํ๋ค.
Copy # appํ๋ก์ ํธ์ serializers.py ์ถ๊ฐ
from rest_framework import serializers
class CommentSerializer ( serializers . Serializer ):
email = serializers . EmailField ()
content = serializers . CharField (max_length = 200 )
created = serializers . DateTimeField ()
Serializing Objects
์์ฑํ Serializer
ํด๋์ค๋ฅผ ์ฌ์ฉํ์ฌ ํด๋น ๊ฐ์ฒด๋ฅผ serializingํ ์ ์๋ค.
Copy >>> from quickstart . serializers import CommentSerializer
>>> c = Comment . objects . get (id = 1 )
>>> serializer = CommentSerializer (c)
>>> serializer . data
{ 'email' : 'test@test.com' , 'content' : 'testsetstsetset' , 'created_at' : '2019-10-08T07:28:15.082464Z' }
์ด ์์ ์์๋ ๋ชจ๋ธ ์ธ์คํด์ค๋ฅผ ํ์ด์ฌ ๊ธฐ๋ณธ ๋ฐ์ดํฐ ์ ํ์ผ๋ก ๋ณํํ ๊ฒ์ด๋ค.
JSON ๋ฐ์ดํฐ๋ก ๋ณํํ๊ธฐ ์ํด์๋ JSONRenderer
๋ฅผ ์ด์ฉํด json์ผ๋ก ๋ฐ๊ฟ์ค๋ค.
Copy >>> from rest_framework . renderers import JSONRenderer
>>> json = JSONRenderer (). render (serializer.data)
>>> json
b '{"email":"test@test.com","content":"testsetstsetset","created_at":"2019-10-08T07:28:15.082464Z"}'
Deserializing Objects
serializing๊ณผ ์ ์ฌํ๋ค. ์ฐ์ json๋ฐ์ดํฐ๋ฅผ ํ์ด์ฌ ๋ฐ์ดํฐ ํ์์ผ๋ก parsingํ ํ์ ๊ธฐ๋ณธ ๋ฐ์ดํฐ๋ฅผ ๊ฒ์ฆ๋ ๋ฐ์ดํฐ๋ก ๋ณต์ํ๋ค.
Copy >>> from django . utils . six import BytesIO
>>> from rest_framework . parsers import JSONParser
>>> stream = BytesIO (json)
>>> data = JSONParser (). parse (stream)
>>> data
{ 'email' : 'test@test.com' , 'content' : 'testsetstsetset' , 'created_at' : '2019-10-08T07:28:15.082464Z' }
Copy >>> serializer = CommentSerializer (data = data)
>>> serializer . is_valid ()
True
>>> serializer . validated_data
OrderedDict([('email', 'test@test.com'), ('content', 'testsetstsetset'), ('created_at', datetime.datetime(2019, 10, 8, 7, 28, 15, 82464, tzinfo=<UTC>))])
Return Instance
์์ ํ ๊ฐ์ฒด ์ธ์คํด์ค๋ฅผ ๋ฐํํ๊ธฐ ์ํด์๋ create()
, update()
๋ฉ์๋๋ฅผ ๊ตฌํํด์ object instance ๋ฐํ์ด ๊ฐ๋ฅํ๋ค.
Copy from rest_framework import serializers
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
๊ฐ์ฒด ์ธ์คํด์ค๊ฐ ๋ชจ๋ธ๊ณผ ์ผ์นํ๋ ๊ฒฝ์ฐ์๋ ์๋์ ๊ฐ์ด ๊ฐ์ฒด๋ฅผ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅ๋๋๋ก ํด์ผํ๋ค.
Copy from rest_framework import serializers
class CommentSerializer ( serializers . Serializer ):
email = serializers . EmailField ()
content = serializers . CharField (max_length = 200 )
created = serializers . DateTimeField ()
# ๋ชจ๋ธ ๊ฐ์ฒด์ ์ผ์นํ๋ ๊ฒฝ์ฐ
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
๋ฐ์ดํฐ๋ deserializingํ ๋ .save()
๋ฅผ ํธ์ถํ์ฌ ์ ํจ์ฑ์ด ๊ฒ์ฌ๋ ๋ฐ์ดํฐ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๊ฐ์ฒด ์ธ์คํด์ค๋ฅผ ๋ฐํํ ์ ์๋ค.
Copy >>> deserializer = CommentSerializer (data = serializer.data)
>>> comment = deserializer . save ()
>>> comment
< Comment : Comment object ( 3 ) >
์ฌ๊ธฐ์ save()
๋ instance๊ฐ ์กด์ฌํ๋ฉด update๋ฅผ ์กด์ฌํ์ง์์ผ๋ฉด create๋ฅผ ํด์ค๋ค.
Copy # .save() will create a new instance.
serializer = CommentSerializer (data = data)
# .save() will update the existing `comment` instance.
serializer = CommentSerializer (comment, data = data)
์ธ์คํด์ค๋ฅผ ์ ์ฅํ๋ ์์ ์ ๋ทฐ ์ฝ๋๊ฐ ๋ฐ์ดํฐ๋ฅผ ์ถ๊ฐํ ์ ์์ด์ผํ๋ฉฐ, ์ด ์ถ๊ฐ ๋ฐ์ดํฐ์๋ ๋ค๋ฅธ ์ ๋ณด๊ฐ ํฌํจ๋ ์ ์๋ค.
Copy >>> serializer . save (writer = request.user)
์ถ๊ฐ๋ ๋ฐ์ดํฐ๋ .create()
ํน์ .update()
๊ฐ ํธ์ถ๋ ๋ validated_data์ ํฌํจ๋๋ค.
Validation
๋ฐ์ดํฐ๋ฅผ deserializerํ ๋ ์ ํจ์ฑ์ด ๊ฒ์ฆ๋ ๋ฐ์ดํฐ์ ์ ๊ทผํ๊ธฐ ์ ์ is_valid()
๋ฅผ ํธ์ถํ๊ฑฐ๋ ๊ฐ์ฒด ์ธ์คํด์ค๋ฅผ ์ ์ฅํด์ผํ๋ค.
Copy >>> deserializer = CommentSerializer (data = { 'email' : 'admin@test.com' , 'content' : 'This is test' })
>>> deserializer . save ()
Traceback (most recent call last):
File "<console>" , line 1 , in < module >
File "/Users/jeongdaye/.pyenv/versions/restful/lib/python3.7/site-packages/rest_framework/serializers.py", line 179, in save
'You must call `.is_valid()` before calling `.save()`.'
AssertionError : You must call ` . is_valid () ` before calling ` . save () ` .
>>> deserializer . is_valid ()
False
>>> deserializer . errors
{ 'created_at' : [ ErrorDetail (string = 'This field is required.' , code = 'required' ) ] }
๋ง์ฝ์ is_valid()
๊ฐ False๋ผ๋ฉด, .errors()
๋ฅผ ํตํด์ ๊ฒฐ๊ณผ ์ค๋ฅ๋ฉ์ธ์ง๋ฅผ ํ์ธํ ์ ์๋ค.
Copy >>> deserializer . is_valid (raise_exception = True )
Traceback (most recent call last):
File "<console>" , line 1 , in < module >
File "/Users/jeongdaye/.pyenv/versions/restful/lib/python3.7/site-packages/rest_framework/serializers.py", line 243, in is_valid
raise ValidationError (self.errors)
rest_framework.exceptions.ValidationError: {'created_at': [ErrorDetail(string='This field is required.', code='required')]}
raise_exception=True
ํ๋๊ทธ๋ฅผ ์ฌ์ฉํด serializers.validationError
์์ธ๋ฅผ ๋ฐ์์ํฌ ์ ์๋ค.
Field ๋ ๋ฒจ ๊ฒ์ฆ
validate_field
serializer ์๋ธ ํด๋์ค์ .validate_<field_name>
๋ฉ์๋๋ฅผ ์ถ๊ฐํด custom field ์ ํจ์ฑ ๊ฒ์ฆ์ ์ง์ ํ ์ ์๋ค.
Copy import re
emailRegex = '^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$'
class CommentSerializer ( serializers . Serializer ):
email = serializers . CharField ()
content = serializers . CharField ()
created_at = serializers . DateTimeField ()
def validate_email ( self , value ):
if (re . search (eamilRegex, value) ) :
return value
else :
raise serializers . ValidationError ( "์ด๋ฉ์ผ ํ์์ด ์๋ชป๋์์ต๋๋ค." )
Copy >>> from quickstart . serializers import CommentSerializer
>>> from datetime import datetime
>>> serializer = CommentSerializer(data={'email':'asdfsdaf', 'content':'asdfasdfasdfasf', 'created_at': datetime.now()})
>>> serializer . is_valid ()
False
>>> serializer . errors
{ 'email' : [ ErrorDetail (string = '์ด๋ฉ์ผ ํ์์ด ์๋ชป๋์์ต๋๋ค.' , code = 'invalid' ) ] }
validate
์ฌ๋ฌ ํ๋์ ๋ํ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ํ๋ ค๋ฉด .validate()
๋ฉ์๋๋ฅผ ์ถ๊ฐํ๋ฉด๋๋ค. ์ด ๋ฉ์๋๋ ํ๋๊ฐ์ dict ์๋ฃํ์ ๋จ์ผ ์ธ์๋ก ์ทจํ๋ค.
Copy class CommentSerializer ( serializers . Serializer ):
email = serializers . CharField ()
content = serializers . CharField ()
created_at = serializers . DateTimeField ()
def validate ( self , data ):
if ( len (data[ 'content' ]) < 10 ) & (data [ 'created_at' ] < timezone . now () ) :
raise serializers . ValidationError ( "error" )
return data
Copy >>> from quickstart . serializers import CommentSerializer
>>> from datetime import datetime
>>> serializer = CommentSerializer(data={'email':'asdfsdaf', 'content':'asdfasdfasdfasf', 'created_at': datetime.now()})
>>> serializer . is_valid ()
True
>>> serializer = CommentSerializer (data = { 'email' : 'test@test.com' , 'content' : 'asf' , 'created_at' : datetime. now ()})
>>> serializer . is_valid ()
False
>>> serializer . errors
{ 'non_field_errors' : [ ErrorDetail (string = 'error' , code = 'invalid' ) ] }
validators
Serializer์ ๊ฐ๋ณํ๋์ validators๋ฅผ ํตํด ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ํ ์ ์๋ค.
Copy from rest_framework import serializers
import re
emailRegex = '^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$'
def email_check ( value ):
if (re . search (emailRegex, value) ) :
return value
else :
raise serializers . ValidationError ( "์ด๋ฉ์ผ ํ์์ด ์๋ชป๋์์ต๋๋ค." )
class CommentSerializer ( serializers . Serializer ):
email = serializers . CharField (validators = [email_check])
content = serializers . CharField ()
created_at = serializers . DateTimeField ()
Copy >>> from quickstart . serializers import CommentSerializer
>>> from datetime import datetime
>>> serializer = CommentSerializer(data={'email':'asdfsdaf', 'content':'asdfasdfasdfasf', 'created_at': datetime.now()})
>>> serializer . is_valid ()
False
>>> serializer . errors
{ 'email' : [ ErrorDetail (string = '์ด๋ฉ์ผ ํ์์ด ์๋ชป๋์์ต๋๋ค.' , code = 'invalid' ) ] }
ModelSerializer
ModelSerializer
ํด๋์ค๋ ๋ชจ๋ธ ํ๋๊ฐ ํฌํจ๋ Serailzer ํด๋์ค๋ฅผ ๊ฐ๋จํ๊ฒ ๋ง๋ค ์ ์๋ค.
๋ชจ๋ธ์ ๊ธฐ๋ฐ์ผ๋ก ์ผ๋ จ์ ํ๋๊ฐ ์๋์ผ๋ก ์์ฑ
unique_together validator
์ ๊ฐ์ serializer์ ๋ํ validator๋ฅผ ์๋ ์์ฑ
.create()
์ .update()
์ ๊ฐ๋จํ ๊ธฐ๋ณธ ๊ตฌํ์ ํฌํจ
Copy from rest_framework import serializers
class CommentSerializer ( serializers . ModelSerializer ):
class Meta :
model = Comment
fields = ( 'email' , 'content' , 'created_at' )
๋ง์ฝ ์ ์ฒด ํ๋๋ฅผ ํฌํจํ๊ณ ์ถ๋ค๋ฉด __all__
์ผ๋ก ์์ฑ์ ์ค์ ํ๋ฉด๋๋ค.
Copy from rest_framework import serializers
class CommentSerializer ( serializers . ModelSerializer ):
class Meta :
model = Comment
fields = '__all__'
Copy >>> from quickstart . serializers import CommentSerializer
>>> serializer = CommentSerializer ()
>>> print ( repr (serializer))
CommentSerializer ():
id = IntegerField (label = 'ID' , read_only = True )
email = CharField (max_length = 100 )
content = CharField (style = { 'base_template' : 'textarea.html' })
created_at = DateTimeField (required = False )
exclude
์์ฑ์ผ๋ก ์ ์ธํ๊ณ ์ถ์ ํ๋๋ง ์ค์ ํ ์๋ ์๋ค.
Copy from rest_framework import serializers
class CommentSerializer ( serializers . ModelSerializer ):
class Meta :
model = Comment
exclude = ( 'email' , 'content' , 'created' )
HyperlinkedModelSerializer
HyperlinkedModelSerializer
ํด๋์ค๋ ๊ธฐ๋ณธ ํค๊ฐ ์๋ ๊ด๊ณ๋ฅผ ๋ํ๋ด๊ธฐ ์ํด ํ์ดํผ๋งํฌ๋ฅผ ์ฌ์ฉํ๋ค ๋ ์ ์ ์ ์ธํ๊ณ ๋ ModelSerialzer
ํด๋์ค์ ์ ์ฌํ๋ค. ๊ธฐ๋ณธ์ ์ผ๋ก ๊ธฐ๋ณธ ํค ํ๋ ๋์ url
ํ๋๊ฐ ํฌํจ๋๋ค.
Copy class CommentSerializer ( serializers . HyperlinkedModelSerializer ):
class Meta :
model = Comment
fields = ( 'url' , 'id' , 'email' , 'content' , 'created_at' )
Copy >>> from quickstart . serializers import CommentSerializer
>>> serializer = CommentSerializer ()
>>> print ( repr (serializer))
CommentSerializer ():
url = HyperlinkedIdentityField (view_name = 'comment-detail' )
id = IntegerField (label = 'ID' , read_only = True )
email = CharField (max_length = 100 )
content = CharField (style = { 'base_template' : 'textarea.html' })
created_at = DateTimeField (required = False )
url field๋ HyperlinkedIdentityField๋ฅผ ์ฌ์ฉํ๋ค.
HyperlinkedModelSerializer
ํด๋์ค๋ฅผ ์ธ์คํด์คํ ํ ๋๋ ํ์ฌ request
๋ฅผ serializer ์ปจํ
์คํธ์ ํฌํจํด์ผํ๋ค.
Copy serializer = AccountSerializer (queryset, context = { 'request' : request})
์ด๋ ๊ฒ ์ ๋ฌํ๋ฉด ์ ๊ทํ๋ URL์ ์ฌ์ฉํ๋ค.
Copy http://api.example.com/accounts/1/
๋ง์ฝ ์๋ URL์ ์ฌ์ฉํ๋ ค๋ฉด {'request': None}
์ ๋ช
์์ ์ผ๋ก ์ ๋ฌํ๋ฉด๋๋ค.
How hyperlinked views are determined
๋ชจ๋ธ์ธ์คํด์ค์ ํ์ดํผ๋งํฌํ๊ธฐ ์ํด ์ด๋ค ๋ทฐ๋ฅผ ์ฌ์ฉํ๋์ง ์๋ ค์ค์ผํ๋ค. ๊ธฐ๋ณธ์ ์ผ๋ก {model_name} -detail
์คํ์ผ๊ณผ view ์ด๋ฆ์ด ์ผ์นํด์ผํ๋ฉฐ, pk
ํค์๋ ์ธ์๋ก ์ธ์คํด์ค๋ฅผ ์ฐพ๋๋ค. ๋ง์ฝ ๋ฐ๋ก ์ง์ ํด์ฃผ๊ณ ์ถ๋ค๋ฉด extra_kwargs
์ค์ ์์ view_name
๋๋ lookup_field
์ต์
์ ์ค์ ํ๋ฉด๋๋ค.
Copy class AccountSerializer ( serializers . HyperlinkedModelSerializer ):
class Meta :
model = Account
fields = ( 'account_url' , 'account_name' , 'users' , 'created' )
extra_kwargs = {
'url' : { 'view_name' : 'accounts' , 'lookup_field' : 'account_name' },
'users' : { 'lookup_field' : 'username' }
}
๋๋ serializer์์ ํ๋๋ฅผ ๋ช
์์ ์ผ๋ก ์ค์ ํ ์ ์๋ค.
Copy class AccountSerializer ( serializers . HyperlinkedModelSerializer ):
url = serializers . HyperlinkedIdentityField (
view_name = 'accounts' ,
lookup_field = 'slug'
)
users = serializers . HyperlinkedRelatedField (
view_name = 'user-detail' ,
lookup_field = 'username' ,
many = True ,
read_only = True
)
class Meta :
model = Account
fields = ( 'url' , 'account_name' , 'users' , 'created' )
์ฐธ๊ณ ๋งํฌ