
- Serializer
- HTTP 요청 메서드와 상태 코드
- API 구현
- Method 기반 구현
- Class 기반 구현
- Mixin을 이용한 구현
- Generics를 이용한 구현
모델 인스턴스나 QuerySet과 같은 데이터를 JSON 형식의 파일로 변환하는 작업. 반대의 작업은
Deserialize라고 한다.
API 통신상에서는 JSON 형식으로 데이터를 주고받는 경우가 많기 때문에 변환 작업이 필요하다.
pip install djangorestframework
/polls_api/serializers.py 작성from rest_framework import serializers
from polls.models import Question
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 기반으로 Question 생성
return Question.objects.create(**validated_data)
def update(self, instance, validated_data):
## instance 기반으로 Question 수정
instance.question_text = validated_data.get('question_text', instance.question_text)
instance.save()
return instance
from polls.models import *
from polls_api.serializers import *
from rest_framework.renderers import JSONRenderer
import json
Serialize# Question 모델 첫 번째 값 추출 후 QuestionSerializer에 담기
q = Question.objects.first()
serializer = QuestionSerializer(q)
serializer.data
# serialize
json_str = JSONRenderer().render(serializer.data)
json_str


Deserializedata = json.loads(json_str)
data

기존에 작성했던 Serializer를 조금 더 간단하게 변경
# /polls_api/serializers.py
from rest_framework import serializers
from rest_framework.renderers import JSONRenderer
from polls.models import Question
# ModelSerializer 이용시 Meta 정보만 작성하면 create, update 등의 메서드 자동 적용
class QuestionSerializer(serializers.ModelSerializer):
class Meta:
model = Question
fields = ('id', 'question_text', 'pub_date')
HTTP 요청 Method
GET : 특정 리소스의 표시를 요청 (read)
POST : 특정 리소스에 새로운 데이터를 생성 요청 (create)
PUT : 특정 리소스에 수정 요청 (update)
DELETE : 특정 리소스에 삭제 요청 (delete)
HTTP Status Code (상태코드)
200번대 : 정상적인 결과
200 : OK201 : CREATED400번대 : 사용자의 잘못된 요청
400 : BAD REQUEST404 : NOT FOUND500번대 : 서버 내부의 오류
/polls_api/views.py 작성from django.shortcuts import render
from rest_framework.decorators import api_view
from polls.models import Question
from polls_api.serializers import QuestionSerializer
from rest_framework.response import Response
# Create your views here.
# JSON 형태의 요청과 응답
@api_view()
def question_list(request):
questions = Question.objects.all()
# JSON으로 변환
serializer = QuestionSerializer(questions, many = True)
return Response(serializer.data)
# /mysite/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('polls/', include('polls.urls')),
path('admin/', admin.site.urls),
# rest 경로 추가 후 polls_api의 url과 연결
path('rest/', include('polls_api.urls')),
]
# /polls_api/urls.py
from django.urls import path
from .views import *
urlpatterns = [
path('question/', question_list, name='question-list'),
]
/polls_api/views.py의 question_list 함수에 POST를 처리하는 부분 작성from django.shortcuts import render
from rest_framework.decorators import api_view
from polls.models import Question
from polls_api.serializers import QuestionSerializer
from rest_framework.response import Response
from rest_framework import status
# Create your views here.
# GET과 POST 메서드를 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)
#POST 요청 처리
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)
- Question 한개에 대한 처리를 하기위해 question_detail 이라는 api를 만들어야 한다. 그 후 필요한 GET, PUT과 같은 메소드를 작성하면 된다.
# 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)
위의 방식은 메서드를 이용해서 api를 구현했지만 class를 기반으로도 구현할 수 있다.
request를 받으면QuestionList클래스의 인스턴스를 만든 뒤 request의 요청이 무엇인지에 따라서get을 호출할 지post를 호출할 지를 결정
1. QuestionList를 Class 형식으로 변경
# CLASS 기반의 API View
class QuestionList(APIView):
def get(self, requust):
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)
2. urls.py 변경
from django.urls import path
from .views import *
urlpatterns = [
# 중간에 메소드가 아닌 클래스가 들어오므로 [클래스명.as_view()] 로 변경
path('question/', QuestionList.as_view(), name='question-list'),
path('question/<int:id>/', QuestionDetail.as_view(), name='question-detail')
]
class 기반의 api로 변경하면서 코드가 더 간결해졌지만, Mixin 과 Generic View를 이용하면 더욱 더 간결하고 가독성 좋게 코드를 작성할 수 있다.
1. Class 변경
# CLASS 기반의 API View / Mixin, GenericAPIView 사용
class QuestionList(mixins.ListModelMixin, mixins.CreateModeMixin, generics.GenericAPIView):
queryset = Question.objects.all()
serializer_class = QuestionSerializer
def get(self, requust, *args, **kwargs):
return self.list(requust, *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, requust, *args, **kwargs):
return self.retrieve(requust, *args, **kwargs)
def put(self, requust, *args, **kwargs):
return self.update(requust, *args, **kwargs)
def delete(self, requust, *args, **kwargs):
return self.destroy(requust, *args, **kwargs)
2. URL 변경
from django.urls import path
from .views import *
urlpatterns = [
path('question/', QuestionList.as_view(), name='question-list'),
# id를 받는 곳에 id가 아닌 pk로 변경해야 됨. Generic APi View는 pk를 이용해서 찾아오기 때문
path('question/<int:pk>/', QuestionDetail.as_view(), name='question-detail')
]
generics를 사용하면 get,post 메서드 등을 따로 구현하지 않아도 되기 때문에 코드를 말도안되게 더 간단하게 작성이 가능하다.
# CLASS 기반의 API View / Mixin, GenericAPIView 사용
class QuestionList(generics.ListCreateAPIView):
queryset = Question.objects.all()
serializer_class = QuestionSerializer
class QuestionDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Question.objects.all()
serializer_class = QuestionSerializer
원래 JAVA 언어와 Spring 을 공부하다 와서그런지 View 라는 것이 사용자의 화면을 제어하는 역할이라는 생각이 계속 들어서 헷갈리는 부분이 많았다. Python Django 에서의 View는 비즈니스 로직까지 포함할 수 있다는 것을 처음에는 좀 이질감이 들었는데 강의를 듣다보니 자연스럽게 받아들이게 된 것 같다.