[DEV] 4주차. 장고 활용한 API서버 만들기(3)
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 요청을 처리할 것이라는 의미
- serializer에 여러 개의 인스턴스를 줄 때
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),
]
개발 과정에서 데이터를 보기 쉽도록 JSON 그려줌
[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)
POST 할 수 있는 공간이 생김
JSON 형태로 데이터 전송
성공하면 status = 200 OK
잘못된 형식으로 POST한 경우 status = 400 Bad Request
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)
PUT
== update
question_text 를 수정함!
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 뷰를 작성할 때 사용됩니다.
예를 들어, ListModelMixin
및 RetrieveModelMixin을
함께 사용하여 모델의 목록을 보여주고 개별 모델을 조회하는 API 뷰를 만들 수 있습니다.
2) generics
모듈:
generics
모듈은 DRF에서 일반적인 API 뷰를 생성하는 데 사용됩니다. 이러한 뷰는 대부분 기본 CRUD (Create, Retrieve, Update, Delete) 작업을 처리하며, 다양한 HTTP 요청 메서드 (GET, POST, PUT, DELETE)를 처리할 수 있습니다.
주요 generics
뷰 클래스에는 다음이 있습니다:
ListCreateAPIView
: 모델의 목록을 나열하고 새로운 모델 인스턴스를 생성하는 뷰를 생성합니다.RetrieveUpdateDestroyAPIView
: 개별 모델 인스턴스를 검색하고 업데이트 및 삭제하는 뷰를 생성합니다.- 다른 일반적인 클래스에는
RetrieveAPIView
,UpdateAPIView
,DestroyAPIView
,CreateAPIView
등이 있습니다.
이러한 일반적인 뷰는 mixins와
함께 사용할 수 있으며, 뷰의 동작을 커스터마이징하고 필요에 맞게 확장할 수 있습니다.
mixins
및 generics를
함께 사용하면, 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 상속받음- 코드 매우 간결!