20230426 TIL - django 프레임워크 (3)

ohyujeong·2023년 4월 27일
0

TIL

목록 보기
13/27
post-thumbnail

📖 오늘의 학습

  • django 프레임워크 : Serializers, HTTP Methods, Class/mixin/generic View

Serializers

직렬화 (Serialize) : 모델 인스턴스나 QuerySet과 같은 데이터를 JSON 형식의 파일로 변환하는 작업

역직렬화 (Deserialize) : JSON 형식의 데이터를 정의된 포맷에 맞춰 다시 모델 인스턴스로 변환하는 작업

JSONRenderer() 는 Serialize된 데이터를 JSON 형식으로 렌더링한다.
json.loads() 는 JSON 형식의 데이터를 딕셔너리 형태로 Deserialize한다.

Serializer

Serializer는 데이터를 직렬화(Serialize)하고 역직렬화(Deserialize)하기 위한 도구이다. 모델 인스턴스를 JSON 형식으로 변환할 수 있고 주로 RESTful API에서 데이터를 전송할 때 사용된다.

Serializer 사용하기

djangorestframework 의 설치가 필요하다. 아래의 명령어로 설치하고 python 코드에서 import하여 사용한다.

pip install djangorestframework

polls/serializers.py

serializers 는 데이터를 저장/수정할 때 항상 데이터의 유효성을 검사하고, 검사를 통과해야만 저장이 가능하다.

from rest_framwork import serializers
from polls.models import Question # serialize 할 모델 가져오기

class QuestionSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    question_text = serializers.CharField(max_length=200)
    pub_date = serializers.DateTimeField(read_only=True)

    # 유효성 검사를 통과한 데이터를 기반으로 저장
    def create(self, validated_data):
        return Question.objects.create(**validated_data)
    
    def update(self, instance, validated_data):
        # validated_data는 ordered dict 형태로 .get() 메소드를 사용할 수 있다
        instance.question_text = validated_data.get('question_text', instance.question_text)
        instance.save()
        return instance

ModelSerializer 사용하기

serializers.Serializer 를 사용하여 모델의 Serializer 클래스를 구현할 수도 있지만 serializers.ModelSerializer 를 사용한다면 좀 더 편리하게 구현할 수 있다.

구현할 클래스 내의 Meta 클래스 내부의 model 속성을 통해 Serialize할 대상 모델을 지정할 수 있고 fields 속성을 통해 Serialize할 대상 필드들을 리스트 형태로 나열하여 지정할 수 있다.

기본적으로 create()update() 메서드가 구현되어 있어, 추가로 코드를 작성해주지 않아도 그 기능을 사용할 수 있다.

polls/serializers.py

from rest_framework import serializers
from polls.models import Question

class QuestionSerializer(serializers.ModelSerializer):
    class Meta:
        model = Question
        fields = ['id','question_text', 'pub_date']

HTTP Methods

주로 사용되는 HTTP Method는 아래와 같다. CRUD 기능을 구현할 수 있다.

  • 데이터 생성(Create) : POST
  • 데이터 조회(Read) : GET
  • 데이터 업데이트(Update) : PUT
  • 데이터 삭제(Delete) : DELETE

view에서 HTTP Method 구현

view에서 HTTP Method를 구현하는 방법은 아래와 같이 다양한 방법이 있다. 복잡한 방법부터 간단한 방법 순서로 질문리스트를 보여주는 view를 구현하는 과정을 알아가 본다.

  1. @api_view 데코레이터 사용
  2. APIView 클래스를 상속받아 클래스 기반의 뷰 생성
  3. mixins 모듈 사용
  4. generics 모듈 사용

1. @api_view 데코레이터 사용

@api_view 데코레이터의 ()가 비어있는 경우에는, GET 요청을 처리한다는 의미이다. 다른 HTTP Method의 사용을 원할 때는 @api_view('GET','POST') 와 같이 명시해주어야 한다. 여러개의 HTTP Method를 사용할 경우 if 문으로 분기해준다.

지난 번 구현했던 view와 구분하기 위해 아래와 같이 mysite/urls.pypolls_api 의 url을 따로 추가해주었다.

# mysite/urls.py

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('polls/',  include('polls.urls')),
    path('rest/', include('polls_api.urls')),
]

polls_api 앱에도 url 파일을 만들어 경로를 찾아갈 수 있게 한다. @api_view 데코레이터를 사용했지만 polls 앱의 url과 달라진 점은 없다.

# polls_api/urls.py

from django.urls import path
from .views import *

# @api_view 데코레이터 사용
urlpatterns = [
    path('question/', question_list, name='question-list'),
    path('question/<int:id>/', question_detail, name='question-detail')
]
# polls_api/views.py

from polls.models import Question
from polls_api.serializers import QuestionSerializer
from rest_framework.response import Response
from rest_framework.decorators import api_view

@api_view('GET','POST')
def question_list(request):
    if request.method == 'GET':
        questions = Question.objects.all()
    	serializer = QuestionSerializer(questions, many = True)
    	return Response(serializer.data)

    if request.method == 'POST':
        serializer = QuestionSerializer(data=request.data)
        if serializer.is_valid(): # is_valid() 메서드는 serializer가 올바른 데이터 형식을 가지고 있는지 확인하고, 필수 필드가 존재하는지 여부를 검증한다.
            serializer.save() # save() 메서드가 실행되면, 데이터베이스에 새로운 질문 객체가 저장된다.
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) # serializer가 유효하지 않은 경우, 에러메시지와 함께 HTTP 응답이 반환한다.

2. APIView 클래스를 상속받아 클래스 기반의 View 생성

클래스 기반의 view에서는 @api_view() 데코레이터를 사용하여 처리할 수 있는 request의 종류 (HTTP method)를 표시해 줄 필요가 없다.

# polls_api/views.py

from rest_framework.views import APIView

class QuestionList(APIView):
    def get(self, request):
        questions = Question.objects.all()
        serializer = QuestionSerializer(questions, many=True)
        return Response(serializer.data)

    def post(self, request):
        serializer = QuestionSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST

함수 기반 뷰와 달리 urls.py 에서 클래스 기반의 뷰의 URL을 정의할 때는 클래스 이름 뒤에 .as_view()를 붙여서 적어주어야 한다.

# polls_api/urls.py

from django.urls import path
from .views import *

# API_View 클래스 상속한 클래스 사용
urlpatterns = [
    path('question/', QuestionList.as_view(), name='question-list'),
    path('question/<int:id>/', QuestionDetail.as_view(), name='question-detail'),
]

3. mixins 모듈 사용

mixins 의 모델 mixin클래스를 상속한 클래스로 view를 구현한다. *args, **kwargs 인자를 받아오고 넘긴다. 구현이 필요없이 request요청별로 정해진 함수를 리턴한다.

# polls_api/views.py

from polls.models import Question
from polls_api.serializers import QuestionSerializer
from rest_framework import mixins
from rest_framework import generics

class QuestionList(mixins.ListModelMixin,
                  mixins.CreateModelMixin,
                  generics.GenericAPIView):
    queryset = Question.objects.all()
    serializer_class = QuestionSerializer

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

class QuestionDetail(mixins.RetrieveModelMixin,
                    mixins.UpdateModelMixin,
                    mixins.DestroyModelMixin,
                    generics.GenericAPIView):
    queryset = Question.objects.all()
    serializer_class = QuestionSerializer

    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

클래스 기반의 뷰의 URL을 정의할 때와 유사하지만
'question/<int:pk>/' 와 같이 id 대신에 pk 라고 명시해야 한다.

# polls_api/urls.py

from django.urls import path
from .views import *

# mixins 사용
urlpatterns = [
    path('question/', QuestionList.as_view(), name='question-list'),
    path('question/<int:pk>/', QuestionDetail.as_view(), name='question-detail'),
]

4. generics 모듈 사용

generics 의 APIView클래스를 상속받아 구현한다. 클래스 안에 querysetserializer_class 의 값을 넣어주면 되어 가장 간단하다. 😎

polls_api/urls.pymixins 모듈 사용 방법과 동일하게 사용한다.

# polls_api/views.py

from polls.models import Question
from polls_api.serializers import QuestionSerializer
from rest_framework import generics

class QuestionList(generics.ListCreateAPIView):
    queryset = Question.objects.all()
    serializer_class = QuestionSerializer

class QuestionDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Question.objects.all()
    serializer_class = QuestionSerializer

📝 주요메모사항


😵 공부하면서 어려웠던 내용

profile
거친 돌이 다듬어져 조각이 되듯

0개의 댓글