금일 학습 내용은 django rest 프레임워크에서 등장하는 새로운 개념인 Serializer
각 용어를 살펴보면 다음과 같다!
+) 일반적으로 API 서버에서 JSON 형식의 데이터를 주고받는다
이전에 생성한 Question 모델을 바탕으로 Serializer를 만들고 각 작업을 잘 수행하는지 확인하기!
절차는 다음과 같다.
- 기존 만들어 놓은 프로젝트에 (django rest framework를 구현할 새로운 앱 설치하기)
- 이후 settings에 INSTALLED_APPS에 새로만든 앱의 Config와 rest_framework 추가해주기
# 아래 코드를 통해 serializer 구현위한 앱설치 진행
$ python manage.py startapp polls_api
# polls_api/serializers.py
from rest_framework import serializers
from polls.models import Question
class QuestionSerializer(serializers.Serializer):
# 자동으로 생성되는 id 필드 포함해 기존 Question 모델에 속하는 필드 다 가져오기
id = serializers.IntegerField(read_only = True)
question_text = serializers.CharField(max_length = 200)
pub_date = serializers.DatetimeField(read_only = True)
# 새로 만듬
# 유효성 검사 통과한 데이터 : validated_data로 주어짐
def create(self, validated_data):
return Question.objects.create(**validated_data)
# 기존에 있던 걸 변경하므로 instance 매개변수 필요
def update(self, instance, validated_data):
# validated_data에 값이 없을수도 있으므로 get(a, b) 형태로 구현
instance.question_text = validated_data.get('question_text', instance.question_text)
instance.save()
return instance
-------------------------------------------------------------------------------------------------------------
# < django shell >
##### Serialize (모델 인스턴스 -> JSON)
from polls.models import Question
from polls_api.serializers import QuestionSerializer
q = question.object.first()
q # (쿼리셋-오브젝트 하나)
# 결과
<Question: 제목: 휴가를 어디서 보내고 싶으세요?, 날짜: 2023-02-05 18:52:59+09:00>
serializer = QustionSerializer(q)
serializer.data
# field_name : value 형태로 결과 출력
{'id': 1, 'question_text': '휴가를 어디서 보내고 싶으세요?', 'pub_date': '2023-02-05T18:52:59Z'}
# JSON 형태로 변환 위해 라이브러리 호출
from rest_framework.renderers import JSONRenderer
json_str = JSONRenderer().render(serializer.data)
json_str
# 결과 (serializer.data 결과가 한글이라서 JSON 변환 시 인코딩된 결과)
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-02-05T18:52:59Z"}'
# Deserialize (JSON -> 모델 인스턴스)
import json
data = json.loads(json_str)
data
# 결과 (JSON TO DICTIONARY)
{'id': 1, 'question_text': '휴가를 어디서 보내고 싶으세요?', 'pub_date': '2023-02-05T18:52:59Z'}
# create
serializer = QuestionSerializer(data = data)
serializer.is_valid()
# 결과 : True
serializer.validated_data
# 결과 read_only 제거하고 보여줌
: OrderedDict([('question_text', '휴가를 어디서 보내고 싶으세요?')])
# 만들어진 instance 유무 여부로 create or update 동작
new_question = serializer.save()
new_question
# 결과
<Question: 제목: 휴가를 어디서 보내고 싶으세요?, 날짜: 2023-02-14 18:46:56.209215+00:00>
# Update
data = {'question_text': '제목수정'}
serializer = QuestionSerializer(new_question, data = data)
# save이전 필수작업
serializer.is_valid()
# 결과 : True
serializer.save()
# 결과
<Question: 제목: 제목수정[시리얼라이저에서 업데이트], 날짜: 2023-04-25 13:15:05.852404+00:00>
# validation이 통과하지 않는 경우
long_text = 'abcd' * 3000
data = {'qustion_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')]}
: 기존 코드 간편화 (필드 일일이 정의 필요 X, create, update 메소드 생성 필요 X)
# polls_api/serializer.py
from rest_framework import serializers
from polls.models import Question
class QuestionSerializer(serializers.ModelSerializer):
class Meta:
# model 이름과 fields명 명시 반드시 필요
model = Question
fields = ['id','question_text', 'pub_date']
# < django shell >
from polls_api.serializers import QuestionSerializer
print(QustionSerializer())
# 결과
QuestionSerializer():
id = IntegerField(read_only=True)
question_text = CharField(max_length=200)
pub_date = DateTimeField(read_only=True)
# create 제대로 작동 하는지 확인
serializer = QuestionSerializer(data={'question_text':'모델시리얼라이저로 만들어 봅니다.'})
# 유효성 검사
serializer.is_valid()
# 결과 : True
serializer.save()
# create 결과 확인 가능
<Question: 제목: 모델시리얼라이저로 만들어 봅니다., 날짜: 2023-02-14 19:41:081444+00:00>
CRUD API 설계 시 기본이 되는 핵심 개념!!
CREATE : POST (새로운 데이터 적용 시 사용!)
READ : GET
UPDATE : PUT (기존에 있던 데이터 변경 시 사용)
DELETE : DELETE
200번대 : 정상처리. 200: OK, 201: CREATED
400번대 : 사용자의 잘못된 요청. 400: BAD REQUEST, 404: NOT FOUND
500번대 : 서버 내부 오류
Qustion에 대해 JSON 형식으로 데이터를 제공하는 API 서버 구현!
-> views.py로 구현 가능
- 정보를 조회하는 기능 : HTTP에서 GET 사용
- 정보를 조회하지 않고 새로운 데이터 생성 : POST 메소드 사용
# 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
# 데코레이터의 () 가 비어있는 경우 GET 요청 처리
@api_view() # quetion_list 뷰는 http 요청을 객체로 받음
# 여러개의 question을 처리한다는 의미 부여를 위해 quetion_list로 함수명 지정
def question_list(request):
questions = Question.objects.all()
# 여러개 인식 위해 many 주기
serializer = QuestionSerializer(questions, many = True)
# 데이터를 HTTP 응답으로 반환
return Response(serializer.data)
### 기존 project_name/urls.py , api/urls에 변경 사항 적용 必
# polls_api/urls.py
from django.urls import path
from .views import *
urlpatterns = [
path('question/', question_list, name='question-list')]
# project/urls.py
# urlpattern 에 아래 내용 추가!
path('rest/', include('polls_api.urls'))
# 결과 - Allow에 GET 추가된 것 확인 가능
question을 만드는 역할
# POST- CREATE 역할
from rest_framework import status
@api_view(['GET', 'POST'])
# 여러개의 question을 처리한다는 의미 부여를 위해 quetion_list로 함수명 지정
def question_list(request):
if request.method == 'GET':
questions = Qustions.objects.all()
serializer = QuestionSerializer(questions, many = True)
return Response(serializer.data)
if request.method == 'POST':
# JSON으로 올라온 요청 serializer에 전달
# 새로만들때는 인스턴스 부여 X, 이후 유효성 검사해 save 할 것.
serializer = QuestionSerializer(data = request.data)
if serializer.is_valid():
serializer.save()
# 생성되었음을 의미하도록 201 부여
return Response(serializer.data, status = status.HTTP_201_CREATED)
# 잘못된 응답에 대해 400_BAD_REQUEST 처리
else:
return Response(serializer.errors, status = status.HTTP_400_BAD_REQUEST)
# 결과 - Allow 에 Post 생긴 것 확인 가능
앞서 여러 qustion에 대해 처리했다면 이번에는 단일 question에 대한 처리가 필요하다
# 하나의 question에 대해 처리
from django.shortcuts import get_object_or_404
@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)
Class 기반으로 위 코드 구현하면 다음과 같다.
from rest_framework.views import APIView
class QuestionList(APIView):
def get(self, request):
questions = Qustion.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)
class QustionDetail(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)
# api/urls.py
## 클래스로 구현한 것 urlpattern
urlpatterns = [
path('question/', QuestionList.as_view(), name = 'question-list'),
path('question/<int:id>', QuestionDetail.as_view(), name = 'question-detail'),
]
✍️ 클래스로 구현한 부분이 데코레이터도 필요가 없어서 조금 더 깔끔한 느낌!!
+) 다양한 클래스를 활용해 (상속을 받을 수 있기에) 반복 코드 필요 없이 쉽게 view를 만들 수 있다.
django rest_framework
### Mixin 활용해 처리
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)
## Mixin 활용해 구현한 URLpattern
urlpatterns = [
path('question/', QuestionList.as_view(), name='question-list'),
# Class와 다른 점은 id가 아닌 pk로 객체를 받아옴!
path('question/<int:pk>/', QuestionDetail.as_view(), name='question-detail'),
]
✍️ 클래스에 비해 각 class 별로 미리 question에 대한 queryset과 serializer를 구현해 놓았기에 훨씬 더 수월하게 구현된 느낌.
훨씬 더 간단하게 구현이 가능하다. 기존 generics.GenericAPIView에서 Generic에 해당하는 부분에 ListCreate, RetrieveUpdateDestory를 지정하여 그냥 처리가 가능하다..
from polls.models import Question
from polls_api.serializers import QuestionSerializer
from rest_framework import generics
# GET, POST 구현
class QuestionList(generics.ListCreateAPIView):
queryset = Question.objects.all()
serializer_class = QuestionSerializer
# GET, PUT, DELETE 구현
class QuestionDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Question.objects.all()
serializer_class = QuestionSerializer
rest_framework 모듈을 불러오는데 밑줄이 계속 뜬다.. install도 했고, 뭐 다해봤는데도 이러네
음... 이유를 좀 찾아봐야 할 것 같고,
확실히 월요일부터 시작해서 가상환경 세팅해서 프로젝트, app 만들어서 템플릿 꾸리고 모델 만들어서 migration하는 작업이 사전에 완성도 있게 이루어져야 쫌 뒷부분이 괜찮을 것 같다는 생각이 든다. 가상 환경 만들고, 기본적으로 구성하는 과정이 제일 어려운 듯😭주말에 무조건 복습 꼭 하기❗❗