From과 Serializer 관점에서 DRF 비교

2022. 7. 13. 15:32강의 정리/Django REST Framework

반응형

Serializer / ModelSeializer

데이터 변환 / 직렬화 지원

  • querySet / Model객체 <-> Native Python 데이터타입, JSON/XML

 

Django의 Form / ModelForm과 유사

  • 유효성 검사와 유효성 검사에 통과한 값들을 데이터베이스에 저장 시켜줌
  • Serializer는 뷰 응답을 생성하는 데에 범용적이고 강력한 방법을 제공
  • ModelSerializer는 Serializer 생성을 위한 Shortcut

 


 

특징 비교

Form / ModelForm

  • HTML 입력폼을 위한 입력에 대한 유효성 검사
  • 주로 Create / Update에 대한 처리에서 활용 → 장고 admin에서 활용
  • CreateView / UpdateView CBV를 통한 뷰 처리 → 단일 뷰

 

Serializer / ModelSerializer

  • 데이터 변환 및 직렬화 지원 (JSON포맷 등)
  • 주로 JSON 포맷 (주된 Web API 포맷) 입력에 대한 유효성 검사
  • List / Create 및 특정 Record(Resource)에 대한 Retrieve / Edit(update or partial_update) / Delete 등에서 활용
  • APIView를 통한 뷰 처리 → 단일 뷰
  • ViewSet을 통한 뷰 처리 → 2개 뷰 → 2개 URL 처리

 


 

주된 호출 주체 비교

Form / ModelForm

  • 일반적으로 웹 브라우저 상에서
    • HTML Form Submit (POST 요청)
    • JavaScript에 의한 비동기 호출
  • 물론 Android/iOS앱에 의한 요청/응답도 가능
    • 모두 http(s) 프로토콜 요청 / 응답이기에

 

 

Serializer / ModelSerializer

  • 다양한 Client에 대한 Data위주의 http(s)요청
    • by: Web / Android / iOS 등

 


 

클래스 정의 비교

Form / ModelForm : Form은 html 폼을 효율적으로 처리하기 위한 목적을 가진다.

from django import forms

class PostForm(forms.Form):
    email = forms.EmailField()
    content = forms.CharField(**widget=forms.Textarea**)
    created_at = forms.DateTimeField()

class PostModelForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = '__all__'

 

Serializer / ModelSerializer : Serializer는 다양한 http 클라이언트로부터의 API 요청을 처리하기 위한 목적을 갖기에, widget과 같은 인자는 없다.

from rest_framework import serializers

# 시리얼라이저/디시리얼라이저 되어야 하는 필드 정의 필요
# create(), update() 메소드 정의 필요
# create(): 시리얼라이저를 대상으로 save() 메소드를 호출하여 DB 인스턴스를 생성할 때의 동작 정의
# update(): 시리얼라이저를 대상으로 save() 메소드를 호출하여 DB 인스턴스를 수정하고자 할 때의 동작 정의

class PostSerializer(serializers.Serializer):
    email = serializers.EmailField()
    content = serializers.CharField(max_length=200)
    created_at = serializers.DateTimeField()

# Model과 필드를 정의
class PostModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = '__all__'

 

 


 

 

FBV를 통한 요청 / 응답 비교 – Form vs Serializer

Form

def post_create(request):
    if request.method == 'POST':
        form = PostForm(request.POST, request.FILES)
        if form.is_valid():
            post = form.save()
            return redirect(post)
    else:
        form = PostForm()
    return render(request, 'myapp/post_form.html', { 
        'form': form,
    })

def post_list_or_create(request):
    # 새 글 저장을 구현
    if request.method == 'POST':
        form = PostForm(request.POST, request.FILES)
        if form.is_valid():
            post = form.save()
            # 커스텀 직렬화 루틴이 필요
            return JsonResponse(post)
        return JsonResponse(form.errors)
    # 목록 응답을 구현
    else:
        qs = Post.objects.all()
        return JsonResponse(qs) # 커스텀 직렬화 루틴이 필요

 

 

 

 

Serializer 

def post_list_or_create(request):
    if request.method == 'POST':
        serializer = PostSerializer(data=request.POST)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=201)
        return Response(serializer.errors, status=400)
    else:
        qs = Post.objects.all()
        serializer = PostSerializer(qs, many=True)
        return Response(serializer.data)

from rest_framework.response import Response
from rest_framework.views import APIView

class PostListCreateAPIView(APIView):
    def get(self, request):
        serializer = PostSerializer(Post.objects.all(), many=True)
        return Response(serializer.data)

    def post(self, request):
        serializer = PostSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=201)
        return Response(serializer.errors, status=400)

 

 


 

CBV를 통한 요청 / 응답 비교 – Form vs Serializer

 

장고 기본

from django.views.generic import ListView, CreateView

post_list = ListView.as_view(model=Post)

post_new = CreateView.as_view(model=Post, form_class=PostModelForm)

# 앱/urls.py
urlpatterns = [
    path('', post_list),
    path('new/', post_new),
]
<form action="" method="post" enctype="multipart/form-data">
    {% csrf_token %}
    <table>
        {{ form.as_table }}
    </table>
    <input type="submit" />
</form>

 

 

DRF의 APIView

from rest_framework.generics import ListCreateAPIView
class PostListCreateAPIView(ListCreateAPIView):
    queryset = Post.objects.all()
    serializer_class = PostModelSerializer

post_list_create = PostListCreateAPIView.as_view()

# 앱/urls.py
urlpatterns = [
# 단일 URL에서 list/create 요청 처리
    path('api/post/', post_list_create),
]

 

 


유효성 검사 수행 시점 – Form vs Serializer

Form

class ProcessFormView(View):
    def get(self, request, *args, **kwargs):
        return self.render_to_response(self.get_context_data())

    def post(self, request, *args, **kwargs):
        form = self.get_form() # POST 데이터를 통해 Form 객체 생성
        if form.is_valid(): # 유효성 검사를 수행. 실패하면 False를 반환
            return self.form_valid(form) # DB로의 저장을 수행
        else:
            return self.form_invalid(form) # 오류 HTML 응답

 

 

 

 

 

Serializer 

# rest_framework/mixins.py
class CreateModelMixin(object):
    def create(self, request, *args, **kwargs): # POST 요청 -> CREATE 요청이 들어오면,
        serializer = self.get_serializer(data=request.data) # POST데이터를 통해 Serializer 인스턴스를 만들고
        serializer.is_valid(raise_exception=True) # 유효성 검사를 수행. 실패하면 예외발생 !!!
        self.perform_create(serializer) # DB로의 저장을 수행
        headers = self.get_success_headers(serializer.data) # 필요한 헤더를 뽑고
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) # 응답을 합니다.

def perform_create(self, serializer): # CREATE 커스텀은 이 함수를 재정의하세요.
serializer.save()

 

 

 


 

커스텀 유효성 검사 루틴 - clean_* vs validate_*

Form -> clean_

from django import forms
class PostForm(forms.Form):
    title = forms.CharField()

    def clean_title(self):
        value = self.cleaned_data.get('title', '')
        if 'django' not in value:
            raise forms.ValidationError('제목에 필히 django가 포함되어야 합니다.')
        return value

 

 

 

Serializer -> validate_

from rest_framework.exceptions import ValidationError

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

    def validate_title(self, value):
        if 'django' not in value:
            raise ValidationError('제목에 필히 django가 포함되어야 합니다.')
        return value
반응형