1. Serializers

  • Serialize
    • 모델 인스턴스나 QuerySet과 같은 데이터를 JSON 형식의 파일로 변환하는 작업
  • Deserialize
    • JSON 형식의 데이터를 정의된 포맷에 맞추어 다시 모델 인스턴스로 변환하는 작업
  • Serializer
    • Serialize, Deserialize 기능을 모두 갖고 있음

polls_api 앱 생성

python manage.py startapp polls_api
  • polls_api/serializers.py
from rest_framework import serializers
from polls.models import Qeustion

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):    # 유효성 검사를 통과한 데이터 : validated_data
        return Question.objects.create(**validated_data)  # validated_data 기반으로 question 만들어짐
    

    def update(self, instance, validated_data):
        instance.question_text = validated_data.get('question_text', instance.question_text) # 값을 못가져올 경우 원래 데이터 유지
        # id, pub_date는 read_only여서 update 불가능
        instance.save()
        return instance

django shell에서

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

q = Question.objects.first()
q
# <Question:  제목: 휴가를 어디서 보내고 싶으세요?, 날짜: 2023-11-01 07:37:20+00:00>

serializer = QuestionSerializer(q)
serializer.data
# {'id': 1, 'question_text': '휴가를 어디서 보내고 싶으세요?', 'pub_date': '2023-11-01T07:37:20Z'}
  • serialize
from rest_framework.renderers import JSONRenderer

json_str = JSONRenderer().render(serializer.data)
json_str
# b'{"id":1,"question_text":"\xed\x9c\xb4\xea\xb0\x80\xeb\xa5\xbc \xec\x96\xb4\xeb\x94\x94\xec\x84\x9c \xeb\xb3\xb4\xeb\x82\xb4\xea\xb3\xa0 \xec\x8b\xb6\xec\x9c\xbc\xec\x84\xb8\xec\x9a\x94?","pub_date":"2023-11-01T07:37:20Z"}'
## string 형태로 인코딩된 JSON
  • deserialize
import json
data = json.loads(json_str)
data
# {'id': 1, 'question_text': '휴가를 어디서 보내고 싶으세요?', 'pub_date': '2023-11-01T07:37:20Z'}

새로운 serializer

  • create
serializer = QuestionSerializer(data=data)
serializer.is_valid()
# True

serializer.validated_data
# OrderedDict([('question_text', '휴가를 어디서 보내고 싶으세요?')])
## id, pub_date는 read_only data여서 유효성 검사를 통과하지 못함

new_question = serializer.save()    ## create 실행
new_question
# <Question: [NEW] 제목: 휴가를 어디서 보내고 싶으세요?, 날짜: 2023-11-03 08:17:14.398835+00:00>

Question.objects.all()
# <QuerySet [<Question:  제목: 휴가를 어디서 보내고 싶으세요?, 날짜: 2023-11-01 07:37:20+00:00>, <Question:  제목: 가장 좋아하는 디저트는?, 날짜: 2023-11-01 07:37:49+00:00>, <Question:  제목: 커피 vs 녹차, 날짜: 2023-11-01 16:21:27.196500+00:00>, <Question:  제목: 휴가 계획이 있나요?, 날짜: 2023-11-01 18:11:04.516555+00:00>, <Question: [NEW] 제목: 새로운 질문, 날짜: 2023-11-02 15:41:51.808307+00:00>, <Question: [NEW] 제목: 휴가를 어디서 보내고 싶으세요?, 날짜: 2023-11-03 08:17:14.398835+00:00>]>
## 새로운 질문이 추가됨
  • update
new_question
# <Question: [NEW] 제목: 휴가를 어디서 보내고 싶으세요?, 날짜: 2023-11-03 08:17:14.398835+00:00>

data = {'question_text':'제목수정'}
serializer = QuestionSerializer(new_question, data=data)
serializer.is_valid()
# True

serializer.save()     ## update 실행
#<Question: [NEW] 제목: 제목수정, 날짜: 2023-11-03 08:17:14.398835+00:00>

validation 통과 x

  • qustion_text의 옵션이었던 max_length=200을 만족하지 않는 데이터
long_text = "abcd"*300
data = {'question_text':long_text}
serializer = QuestionSerializer(data=data)
serializer.is_valid()
#False

serializer.errors
# {'question_text': [ErrorDetail(string='Ensure this field has no more than 200 characters.', code='max_length')]}

2. ModelSerializer

  • polls_api/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']
  • django shell
from polls_api.serializers import QuestionSerializer
print(QuestionSerializer())
# QuestionSerializer():
#     id = IntegerField(label='ID', read_only=True)
#     question_text = CharField(label='질문', max_length=200)
#     pub_date = DateTimeField(label='생성일', read_only=True)

serializer = QuestionSerializer(data={'question_text':'모델시리얼라이저로 만들기'})
serializer.is_valid()
#True

serializer.save()
# <Question: [NEW] 제목: 모델시리얼라이저로 만들기, 날짜: 2023-11-03 08:51:50.424419+00:00>

3. HTTP Methods

https://developer.mozilla.org/ko/docs/Web/HTTP/Methods

  • CRUD API 설계의 기본!

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

4. GET

  • JSON 형식으로 데이터 보여주기

  • polls_api/views.py

    • serializer에 여러 개의 인스턴스를 줄 때 many=True 옵션 사용
    • @api_view()
      • 괄호 안에 아무것도 없으면 quesion_list가 GET 요청을 처리할 것이라는 의미
from django.shortcuts import render
from rest_framework.decorators import api_view
from polls_api.serializers import QuestionSerializer
from rest_framework.response import Response
from polls.models import Question

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


  • polls_api/urls.py
from django.urls import path
from .views import *

urlpatterns = [
    path('question/', question_list, name='question-list'),
]


  • mysite/urls.py
    • path('rest/', include('polls_api.urls')), 추가
from django.contrib import admin
from django.urls import path, include

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

스크린샷 2023-11-03 오후 8 05 55

개발 과정에서 데이터를 보기 쉽도록 JSON 그려줌

스크린샷 2023-11-03 오후 8 06 59

[GET] - [JSON] 누르면 실제로 구현한 원본 api를 볼 수 있음

5. POST

  • polls_api/views.py
    • @api.view() 에 POST 추가
    • GET과 POST 기능을 나누어서 작성
from django.shortcuts import render
from rest_framework.decorators import api_view
from polls_api.serializers import QuestionSerializer
from rest_framework.response import Response
from rest_framework import status
from polls.models import Question

@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():  # 항상 먼저 해야함!
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

스크린샷 2023-11-03 오후 8 32 07

POST 할 수 있는 공간이 생김
JSON 형태로 데이터 전송
성공하면 status = 200 OK

스크린샷 2023-11-03 오후 8 32 45

잘못된 형식으로 POST한 경우 status = 400 Bad Request

스크린샷 2023-11-03 오후 8 36 40

스크린샷 2023-11-03 오후 8 37 15

POST를 성공적으로 한 경우 200 OK도 괜찮지만, 더 정확하게 하기 위해 status = 201 CREATE 로 나타내줌!
polls_api/views.py 에서 설정

6. PUT, DELETE

  • polls_api/views.py
@api_view(['GET', 'PUT', 'DELETE'])
def question_detail(request, id):
    question = get_object_or_404(Question, pk=id)

    if request.method == 'GET':
        serializer = QuestionSerializer(question)
        return Response(serializer.data)
    if request.method == 'PUT':
        serializer = QuestionSerializer(question, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_200_OK)
        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    if request.method == 'DELETE':
        question.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

스크린샷 2023-11-03 오후 10 38 50

PUT == update
question_text 를 수정함!

스크린샷 2023-11-03 오후 10 42 04

DELETE 질문 삭제!

7. Class 기반 view

  • if문이 아닌, 메소드로 각 기능을 확실하게 구분할 수 있음
  • @api_view() 데코레이터를 사용하지 않아도 됨

  • polls_api/views.py
from django.shortcuts import render, get_object_or_404
from rest_framework.decorators import api_view
from polls_api.serializers import QuestionSerializer
from rest_framework.response import Response
from rest_framework import status
from polls.models import Question
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)
        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


class QuestionDetail(APIView):
    def get(self, request, id):
        question = get_object_or_404(Question, pk=id)
        serializer = QuestionSerializer(question)
        return Response(serializer.data)

    def put(self, request, id):
        question = get_object_or_404(Question, pk=id)
        serializer = QuestionSerializer(question, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_200_OK)
        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, id):
        question = get_object_or_404(Question, pk=id)
        question.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)


  • polls_api/urls.py
from django.urls import path
from .views import *

urlpatterns = [
    path('question/', QuestionList.as_view(), name='question-list'),
    path('question/<int:id>/', QuestionDetail.as_view(), name='question-detail'),
]
  • 웹페이지에서 기능이 똑같이 잘 작동함!

8. Mixin

  • polls_api/views.py
from django.shortcuts import render, get_object_or_404
from rest_framework.decorators import api_view
from polls_api.serializers import QuestionSerializer
from rest_framework.response import Response
from rest_framework import status, mixins, generics
from polls.models import Question
from rest_framework.views import APIView

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)
  • polls_api/urls.py
    • <int:id> -> <int:pk>로 수정
from django.urls import path
from .views import *

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

mixins, generics

1) mixins 모듈:

mixins 모듈은 DRF에서 사용자 정의 API 뷰를 작성할 때 재사용 가능한 메서드와 속성을 제공합니다. 주요 목표는 코드 재사용과 일반적인 API 뷰 기능을 간단하게 추가하는 것입니다. 몇 가지 주요 믹스인 클래스와 사용법은 다음과 같습니다:

  • ListModelMixin: 모델의 목록을 나열하는 API 뷰를 작성할 때 사용됩니다.
  • CreateModelMixin: 새로운 모델 인스턴스를 생성하는 API 뷰를 작성할 때 사용됩니다.
  • RetrieveModelMixin: 개별 모델 인스턴스를 검색하는 API 뷰를 작성할 때 사용됩니다.
  • UpdateModelMixin: 모델 인스턴스를 업데이트하는 API 뷰를 작성할 때 사용됩니다.
  • DestroyModelMixin: 모델 인스턴스를 삭제하는 API 뷰를 작성할 때 사용됩니다.

예를 들어, ListModelMixinRetrieveModelMixin을 함께 사용하여 모델의 목록을 보여주고 개별 모델을 조회하는 API 뷰를 만들 수 있습니다.


2) generics 모듈:

generics 모듈은 DRF에서 일반적인 API 뷰를 생성하는 데 사용됩니다. 이러한 뷰는 대부분 기본 CRUD (Create, Retrieve, Update, Delete) 작업을 처리하며, 다양한 HTTP 요청 메서드 (GET, POST, PUT, DELETE)를 처리할 수 있습니다. 주요 generics 뷰 클래스에는 다음이 있습니다:

  • ListCreateAPIView: 모델의 목록을 나열하고 새로운 모델 인스턴스를 생성하는 뷰를 생성합니다.
  • RetrieveUpdateDestroyAPIView: 개별 모델 인스턴스를 검색하고 업데이트 및 삭제하는 뷰를 생성합니다.
  • 다른 일반적인 클래스에는 RetrieveAPIView, UpdateAPIView, DestroyAPIView, CreateAPIView 등이 있습니다.

이러한 일반적인 뷰는 mixins와 함께 사용할 수 있으며, 뷰의 동작을 커스터마이징하고 필요에 맞게 확장할 수 있습니다.

mixinsgenerics를 함께 사용하면, Django REST framework를 사용하여 API를 개발하는 과정을 단순화하고 재사용 가능한 코드를 작성하는 데 도움이 됩니다.


2-1) generics.GenericAPIView

generics.GenericAPIView는 Django REST framework (DRF)에서 API 뷰를 작성하는 데 사용되는 기본 클래스 중 하나입니다. 이 클래스는 DRF의 generics 모듈에서 제공되며, 일반적인 CRUD (Create, Retrieve, Update, Delete) 작업을 수행하기 위한 API 뷰를 작성할 때 사용됩니다. GenericAPIView는 여러 DRF의 다른 클래스 및 믹스인과 함께 사용하여 API 뷰를 정의하고 구성하는 데 도움이 됩니다.

GenericAPIView의 주요 특징과 메서드는 다음과 같습니다:

1) HTTP 메서드 처리:

  • get(self, request, *args, **kwargs): HTTP GET 요청을 처리하며, 개별 모델 인스턴스를 검색합니다.
  • post(self, request, *args, **kwargs): HTTP POST 요청을 처리하며, 새로운 모델 인스턴스를 생성합니다.
  • put(self, request, *args, **kwargs): HTTP PUT 요청을 처리하며, 모델 인스턴스를 업데이트합니다.
  • delete(self, request, *args, **kwargs): HTTP DELETE 요청을 처리하며, 모델 인스턴스를 삭제합니다.

2) Queryset 및 모델 클래스 지정:

  • queryset: 이 속성은 API 뷰가 작업할 모델 인스턴스들의 QuerySet을 지정합니다.
  • model: 이 속성은 API 뷰가 작업할 모델 클래스를 지정합니다.

3) Serializer 클래스 지정:

  • serializer_class: 이 속성은 API 뷰에서 사용할 시리얼라이저 클래스를 지정합니다. 시리얼라이저는 모델 인스턴스와 JSON 또는 다른 형식의 데이터 간 변환을 처리합니다.

4) 기타 설정 옵션:

  • lookup_field: 모델 인스턴스를 고유하게 식별하는 데 사용할 필드를 지정합니다. 기본값은 “pk”(기본 키)입니다.
  • lookup_url_kwarg: URL에서 모델 인스턴스를 검색하는 데 사용되는 키워드 인수 이름을 지정합니다. 기본값은 None이며, lookup_field로 설정한 필드를 사용합니다.

GenericAPIView를 상속하여 사용자 지정 API 뷰를 작성할 때, 위의 속성과 메서드를 오버라이드하고 필요에 따라 확장할 수 있습니다. 이를 통해 단일 클래스로 여러 종류의 API 뷰를 작성하고 일반적인 CRUD 작업을 수행할 수 있습니다. 예를 들어, ListCreateAPIView, RetrieveUpdateDestroyAPIView 등의 DRF의 일반적인 API 뷰 클래스는 GenericAPIView를 기반으로 작성됩니다.

9. Generic API View

  • polls_api/views.py
from polls_api.serializers import QuestionSerializer
from rest_framework import generics
from polls.models import Question

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


class QuestionDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Question.objects.all()
    serializer_class = QuestionSerializer
  • generics.ListCreateAPIView : mixins의 GET, POST 메서드 상속받음
  • generics.RetrieveUpdateDestroyAPIView : mixins의 GET, PUT, PATCH, DELETE 상속받음
  • 코드 매우 간결!