[Django REST framework] (2) Serializer, Viewset, Router
μ§λ κΈ
[Django REST framework] (1) νλ‘μ νΈ μμ±κ³Ό μ€μ
Django REST frameworkλ₯Ό μ΄μ©ν΄ κ°λ°ν λ λ±μ₯νλ μ£Όμ μ»΄ν¬λνΈμ λν΄μ μ΄ν΄νκ³ μ¬μ©λ²μ μμ보μ.
Serializer
Serializers allow complex data such as querysets and model instances to be converted to native Python datatypes that can then be easily rendered into
JSON,XMLor other content types. Serializers also provide deserialization, allowing parsed data to be converted back into complex types, after first validating the incoming data.
Django REST framework API Guide - Serializer
Serializerλ μ΄λ¦μμ μ§μν μ μλ―μ΄ νΉμ ν ꡬ쑰μ λ°μ΄ν°(κ°μ²΄, Queryset λ±)λ₯Ό Pythonμ dictionaryλ‘ λ³ννλ κΈ°λ₯(μ§λ ¬ν, Serialization)μ μ 곡νλ€. μμΌλ‘ Pythonμ dictionaryλ₯Ό μλ ꡬ쑰μ λ°μ΄ν°λ‘ μλ³ννλ(μμ§λ ¬ν, Deserialization) κΈ°λ₯λ μ 곡νλ€.
Serializer μ μμ μ§λ ¬ν(Serialization)
κ²μκΈμ μ 보λ₯Ό νννλ PostλΌλ μ΄λ¦μ ν΄λμ€κ° μλ€.
class Post
def __init__(self, title, content):
self.title = title
self.content = content
μ΄ post κ°μ²΄μ νλ titleκ³Ό contentλ₯Ό ν΅ν΄ κ²μκΈμ μ λͺ©κ³Ό λ΄μ© κ°μ μ΄μ©ν μ μλ€.
>>> post = Post(title='This is post', content='Hello world!')
>>> post.title
'This is post'
>>> post.content
'Hello world!'
μ΄λ₯Ό μ§λ ¬ννλ Serializerλ₯Ό λ€μκ³Ό κ°μ΄ μ μν μ μλ€.
from rest_framework import serializers
class PostSerializer(serializers.Serializer):
title = serializers.CharField()
content = serializers.CharField()
Serializerλ₯Ό μ΄μ©ν΄ κ°μ²΄μ ꡬ쑰μ μ μ¬ν dictionary κ°μ²΄λ₯Ό μ»μ μ μλ€. Serializerμ νλλ₯Ό μ μν λ λ³λ€λ₯Έ μ΅μ
μ΄ μλ€λ©΄ λμΌν νλ μ΄λ¦μ μ΄μ©ν΄ μμ± μ μ λ¬λ κ°μ²΄(post)μ νλ κ°μ μ΄μ©νλ€.
>>> serializer = PostSerializer(post)
>>> serializer.data
{'title': 'This is post', 'content': 'Hello world!'}
μλμ post κ°μ²΄μ λΉκ΅ν΄λ³΄μ. νλ μ΄λ¦μ ν΅ν΄ κ°μ μ΄μ©νλ κ°μ²΄μ μ μ¬ν ꡬ쑰μ dictionaryλ₯Ό μ»μ μ μμμ νμΈν μ μλ€.
>>> # post κ°μ²΄
>>> post.title
'This is post'
>>> post.content
'Hello world!'
>>> # μ§λ ¬νλ λ°μ΄ν° (dictionary)
>>> serializer.data['title']
'This is post'
>>> serializer.data['content']
'Hello world!'
titleκ³Ό contentλΌλ λμΌν μ΄λ¦μ νλλ₯Ό μ΄μ©ν μ μλ λ€μ Modelμ μΈμ€ν΄μ€λ Serializerλ₯Ό μ΄μ©ν΄ μ§λ ¬νλ₯Ό ν μ μλ€.
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=1024)
content = models.TextField()
>>> post = Post.objects.create(title='This is post', content='Hello world!')
>>> serializer = PostSerializer(post)
>>> serializer.data
{'title': 'This is post', 'content': 'Hello world!'}
μμ§λ ¬ν(Deserialization)μ κ°μ²΄ μ μ₯
Serializerλ μ§λ ¬νμ λ°λλ‘ dictionary κ°μ²΄λ₯Ό μ΄μ©ν΄ μλμ κ°μ²΄λ₯Ό μ»μ΄λ΄λ μμ§λ ¬νμ κΈ°λ₯λ μ 곡νλ€. μ§λ ¬ν κ³Όμ κ³Ό λ¬λ¦¬ μμ§λ ¬νλ₯Ό μν΄μ Serializerλ₯Ό μμ±ν λ data μΈμλ‘ dictionary κ°μ²΄λ₯Ό λ°λμ μ λ¬ν΄μΌ νλ€. Serializerλ₯Ό μμ± ν λ€μλ is_valid()ν¨μλ₯Ό νΈμΆν΄ κ° νλμ μ 체 λ°μ΄ν°μ μ ν¨μ±μ κ²μ¦ ν΄μΌνλ€. validated_dataλ κ°μ²΄μ μμ±, μ μ₯, μμ λ±μ μμ
μ μ΄μ©λλ©°, is_valid()ν¨μλ₯Ό ν΅ν΄ μ ν¨μ±μ κ²μ¦νμ§ μμΌλ©΄ ν΄λΉ μμ
μ μ§νν μ μλ€. μ¦ κ°μ²΄μ μμ±, μ μ₯, μμ μ μμ λ°λμ is_valid()ν¨μλ₯Ό νΈμΆν΄μΌ νλ€.
>>> post_data
{'title': 'This is post', 'content': 'Hello world!'}
>>> serializer = PostSerializer(data=post_data)
>>> serializer.is_valid()
True
>>> serializer.validated_data
{'title': 'This is post', 'content': 'Hello world!'}
κ°μ²΄μ μμ±κ³Ό μ μ₯μ μν΄μ create(), update()λ₯Ό μ€λ²λΌμ΄λ ν΄μΌνλ€. λ ν¨μλ μΈμλ‘ validated_dataλ₯Ό μ λ¬λ°λλ€. μ΄λ is_valid()ν¨μλ₯Ό ν΅ν΄ μ ν¨μ±μ΄ λͺ¨λ κ²μ¦λ κ°μ dictionaryμ΄λ€. create()λ validated_dataλ₯Ό μ΄μ©ν΄ μμ±ν κ°μ²΄λ₯Ό λ°ννλλ‘, κ·Έλ¦¬κ³ update()λ validated_dataλ₯Ό μ΄μ©ν΄ instanceλ₯Ό μμ ν λ€ λ°ννλλ‘ μ μνλ€.
class PostSerializer(serializers.Serializer):
title = serializers.CharField()
content = serializers.CharField()
def create(self, validated_data):
return Post(
title=validated_data['title'],
content=validated_data['content']
)
def update(self, instance, validated_data):
instance.title = validated_data.get('title', instance.title)
instance.content = validated_data.get('content', instance.content)
return instance
Serializerλ Modelκ³Ό ν¨κ» μ¬μ©λλ κ²½μ°κ° λ§λ€. μλλ₯Ό μμ μ½λμ λΉκ΅νμλ©΄ model managerλ₯Ό μ΄μ©ν΄ model instanceλ₯Ό μμ±ν΄ λ°ννλ λΆλΆ, μμ μ΄ν save()λ₯Ό μ΄μ©ν΄ μ μ©ν λΆλΆμ΄ μΆκ°λμλ€.
class PostSerializer(serializers.Serializer):
title = serializers.CharField()
content = serializers.CharField()
def create(self, validated_data):
return Post.objects.create(
title=validated_data['title'],
content=validated_data['content']
)
def update(self, instance, validated_data):
instance.title = validated_data.get('title', instance.title)
instance.content = validated_data.get('content', instance.content)
isinstance.save()
return instance
μμ λ§νλ μ ν¨μ± κ²μ¬λ₯Ό νλλ³λ‘, λν κ°μ²΄ μ 체 μμ€μμ μ€λ²λΌμ΄λν μ μλ€. νΉμ νλμ λν μ ν¨μ± κ²μ¬λ₯Ό μ€λ²λΌμ΄λ νκ³ μ νλ€λ©΄, validate_${field_name}(self, value)λ₯Ό μ€λ²λΌμ΄λνλ©΄ λλ€. λ§μ½ μ λͺ©(title)μ κΈΈμ΄κ° 100 μ΄νμΈ κ²½μ°μ λν΄μλ§ μ ν¨νλ€ νμ νκ³ μΆλ€λ©΄ λ€μμ²λΌ ꡬννλ€. μ ν¨νλ€λ©΄ ν΄λΉ κ°μ λ°ννκ³ κ·Έλ μ§ μλ€λ©΄ μμΈλ₯Ό λμ§λλ‘ κ΅¬ννλ€.
def validate_title(self, value):
if len(value) <= 100:
return value
raise ValueError('μ λͺ©μ κΈΈμ΄λ 100μ΄νμ¬μΌ ν©λλ€.')
ViewSet
Django REST framework allows you to combine the logic for a set of related views in a single class, called a
ViewSet
Django REST framework API Guide - Viewsets
ViewSet μμ class-based viewμ΄λ HTTP method κΈ°λ°μ νΈλ€λ¬λ₯Ό μ 곡νλ APIViewμ λ¬λ¦¬ μμμ λν λμ κΈ°λ°μ νΈλ€λ¬μΈ list, create, retrieve λ±μ μ 곡νλ€.
μμ μμ μμ μ΄μ©νλ Post modelμ κ³μ μ΄μ©νμ.
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=1024)
content = models.TextField
Post modelμ μμ±, μ‘°ννλ ViewSetμ λ€μμ²λΌ μ μνλ€.
from rest_framework.viewsets import ViewSet
class PostViewset(ViewSet):
def create(self, request):
serializer = PostSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data)
def list(self, request):
queryset = Post.objects.all()
serializer = PostSerializer(queryset, many=True)
return Response(serializer.data)
def retrieve(self, request, pk=None):
queryset = User.objects.all()
serializer = UserSerializer(user)
return Response(serializer.data)
ViewSetμμ μ 곡νλ λμμ λ€μκ³Ό κ°λ€.
from rest_framework.viewsets import ViewSet
class PostViewset(ViewSet):
def list(self, request):
pass
def create(self, request):
pass
def retrieve(self, request, pk=None):
pass
def update(self, request, pk=None):
pass
def partial_update(self, request, pk=None):
pass
def destroy(self, request, pk=None):
pass
ViewSetμμ as_view ν¨μλ₯Ό μ΄μ©ν΄ νΉμ viewλ₯Ό μΆμΆν μ μλ€. HTTP methodμ λμμ μ΄λ¦μ΄ λ§€νλ dictionaryλ₯Ό μΈμλ‘ μ λ¬νλ€.
post_list = PostViewset.as_view({'get': 'list', 'post': 'create'})
post_detail = PostViewset.as_view({'get': 'retrieve'})
μμμ μΆμΆν viewλ₯Ό μ΄μ©ν΄ URLConfλ₯Ό μ€μ ν μ μλ€.
urlpatterns = [
...
path('posts/', post_list),
path('posts/<int:pk>', post_detail),
...
]
Router
ViewSetμ μ΄μ©ν΄ viewλ₯Ό μΆμΆνλ κ³Όμ μ μκ°ν΄λ³΄μ. μμμ λν CRUDλ₯Ό μ 곡νλ APIμ μμ£Ό λ±μ₯νλ URL ν¨ν΄κ³Ό λμμ΄ μμμ μ μΆν μ μλ€. κ·Έλ¦¬κ³ μ΄λ¬ν ν¨ν΄μ λ§μΆμ΄ viewμ URL ν¨ν΄μ κΈ°λ³ΈμΌλ‘ λ§€μΉν΄μ£Όλ νΈμ κΈ°λ₯μ Routerκ° μ 곡νλ€. λ€μ λ μ½λλ λμΌν λμμ μννλ€.
from posts.views import PostViewSet
post_list = PostViewSet.as_view({
'get': 'list',
'post': 'create'
})
post_detail = PostViewSet.as_view({
'get': 'retrieve',
'put': 'update',
'patch': 'partial_update',
'delete': 'destroy'
})
urlpatterns = [
path('posts/', post_list),
path('posts/<int:pk>', post_detail),
]
from django.urls import include, path
from rest_framework.routers import DefaultRouter
from posts.views import PostViewSet
router = DefaultRouter()
router.register(r'posts', PostViewSet)
urlpatterns = [
path('', include(router.urls)),
]
REST framework λ§μ μ΄μ©ν κ°λ¨ν μμ
μμμ μ€λͺ
ν μ»΄ν¬λνΈλ€μ μΌλ°μ μΌλ‘ Modelκ³Ό ν¨κ» μ¬μ©λλ κ²½μ°κ° λ§μΌλ©°, Modelμ μ΄μ©ν΄ ν΄λΉ μ»΄ν¬λνΈλ€μ μ μν λ μμ£Ό λ±μ₯νλ ν¨ν΄λ€μ΄ 미리 μ μλμμλ€. μ΄λ₯Ό μ΄μ©ν΄ μμ£Ό μ§§μ μ½λλ§μΌλ‘λ μνλ κΈ°λ₯μ APIλ₯Ό κΈλ°© ꡬνν μ μλ€.
μμμ μ¬μ©νλ Post modelμ CRUDλ₯Ό ν μ μλ APIλ₯Ό μμ±ν΄λ³΄μ.
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=1024)
content = models.TextField()
from rest_framework import serializers
from models import Post
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fiedls = ['id', 'title', 'content']
from rest_framework.viewsets import ModelViewSet
from posts.serializers import PostSerializer
from models import Post
class PostViewSet(ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
from django.urls import include, path
from rest_framework.routers import DefaultRouter
from posts.views import PostViewSet
router = DefaultRouter()
router.register(r'posts', PostViewSet)
urlpatterns = [
path('', include(router.urls)),
]