model 인스턴스나 QuerySet과 같은 데이터를 JSON 형식의 파일로 변환하는 작업Deserialize는 반대로 JSON 형식의 파일을 model 인스턴스나 QuerySet으로 변환하는 작업이다. 1) API 생성
cmd 창에 python manage.py startapp api_name 명령어를 통해 API를 생성해 준다.
생성하면 다음과 같이 polls_api (내가 설정해 준 api_name)가 생성된 것을 확인할 수 있다. 2) serializer 만들기
api 폴더에 새로운 파일 serializers.py를 추가해 준다. (serializers를 사용하기 위해)serialize를 해 주기 위해서는 먼저 model의 column들을 모두 serializers 작업을 해 주어야 한다.from rest_framework import serializers
from polls.models import Question
class QuestionSerializer(serializers.Serializers):
id = serializers.IntegerField(read_only= True)
question_text = serializers.CharField(max_length=200)
pub_date = serializers.DateTimeField(read_only = True)
Django Shell을 통해 구현한 Serializer가 어떻게 동작하는지 확인할 수 있다.serializer을 할 때는 처음 들어가는 데이터면 create, 이미 들어갔던 데이터라면 update 함수를 타게 된다.serializer를 한 값을 그냥 .save()로 저장하거나 validated_data를 확인하려 하면 오류가 발생한다. -> .is_valid()를 통해 유효한 데이터인지 확인해 주어야 한다.from polls.models import Question #Question 모델을 serializer 해 줄 것이기 때문에 import
from polls_api.serializers import QuestionSerializer #QuestionSerializer을 사용해 줄 것이기 때문에 import
q = Question.objects.first() #첫 번째 Question 데이터를 추출
q
>> <Question: 제목: 가장 좋아하는 계절은?, 날짜: 2023-04-24 17:16:45+00:00>
serializer = QuestionSerializer(q)
serializer.data #serializer 된 데이터를 보여 줄 때는 .data를 사용한다.
>> {'id': 1, 'question_text': '가장 좋아하는 계절은?', 'pub_date': '2023-04-24T17:16:45Z'}
from rest_framework.renderers import JSONRenderer #JSON으로 render 해 주기 위해 import
json_str = JSONRenderer().render(serializer.data)
json_str
>> b'{"id":1,"question_text":"\xea\xb0\x80\xec\x9e\xa5 \xec\xa2\x8b\xec\x95\x84\xed\x95\x98\xeb\x8a\x94 \xea\xb3\x84\xec\xa0\x88\xec\x9d\x80?","pub_date":"2023-04-24T17:16:45Z"}'
#아마 한글이라 JSON으로 바뀐 값이 저렇게 표현된 것으로 보임.
import json
data = json.loads(json_str)
data
>> {'id': 1, 'question_text': '가장 좋아하는 계절은?', 'pub_date': '2023-04-24T17:16:45Z'}
#json을 다시 loads 해 주면 처음 표현된 방식으로 돌아감. Deserialize
serializer = QuestionSerializer(data=data) #serializer에 QuestionSerializer된 data를 넣어 줌. 이때 data는 처음 들어가는 것이기 때문에 create
serializer.is_valid()
> True
new_question = serializer.save() #이때 create가 되어 처음 질문이 한 번 더 추가되었음을 알 수 있다.
data = {'question_text': '바다vs산'}
serializer = QuestionSerializer(new_question, data=data) #기존의 데이터에 serializer를 할 경우 update
serializer.is_valid()
>True
serializer.validated_data
> OrderedDict([('question_text', '바다vs산')])
long_text = "abcd"*399
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')]}
.errors를 통해 오류가 발생 시 해당 오류를 확인할 수 있는데 다음과 같이 code=max_length가 200 characters를 넘어 False라는 것을 알려 준다.ModelSerializer은 create와 update 기능을 기본적으로 제공하기 때문에 코드로 구현할 필요가 없다.serializer보다 코드가 더 간단하다from rest_framework import serializers
from polls.models import Question
class QuestionSerializer(serializers.ModelSerializer):
class Meta:
model = Question
fields = ['id', 'question_text', 'pub_date']
1) GET
GET은 데이터를 조회해 오는 것을 말하며 READ에 해당한다. api 쪽의 views.py를 먼저 수정해 준다.Question 목록을 보여 주기 위해 Question 모델에 있는 모든 데이터를 가지고 와 serializer 해 주었다.QuestionSerializer(serializer할 변수, many=True)와 같이 many를 True로 설정해 주면 serializer 해야 하는 값이 여러 개임을 알 수 있게 해 준다.from django.shortcuts import render
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() #괄호 안에 안 넣더라도 question_list가 get 명령을 처리한다
def question_list(request):
questions = Question.objects.all()
serializer = QuestionSerializer(questions, many = True)
#serializer를 여러 개 줄 때는 many 옵션을 줘 serializer 해야 하는 게 여러 개임을 인식하게 해 준다.
api 쪽의 urls.py를 구현해 준다.url이 없으면 이동이 불가하기 때문이다. from django.urls import path
from .views import *
urlpatterns = [
path('question/', question_list, name='question-list')
]
mysite의 urls.py에도 api와 이어질 수 있는 url을 추가해 준다.from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('rest/', include('polls_api.urls')), #api url 추가
path('polls/', include('polls.urls')),
path('admin/', admin.site.urls),
]
2) POST
POST는 데이터를 처음 생성하는 것을 말하며 CREATE에 해당한다.views.py에서 @api_view()에 POST를 추가해 준다. (아무것도 입력하지 않으면 GET)POST와 GET은 다른 로직을 타야 하므로 request.method을 통해 입력된 것이 GET인지 POST인지 확인한다.serialize된 데이터를 저장할 시에는 is_valid를 통해 유효성 검사를 해 주어야 하며 True인 경우 값을 저장하고, False인 경우 errors를 통해 무슨 오류인지를 표출해 준다.Response를 통해 status 값을 정해 줄 수 있다. 이를 해 주는 이유는 만약 잘못된 데이터라 유효성 검사에서 실패한 경우에도 .errors로 오류 메시지만 표시해 주기 때문에 status를 입력하지 않으면 정상적으로 처리됐다는 status = 200 상태가 된다.@api_view(['GET', 'POST'])
def question_list(request):
if request.method == 'GET':
questions = Question.objects.all()
serializer = QuestionSerializer(questions, many = True)
#serializer를 여러 개 줄 때는 many 옵션을 줘 serializer 해야 하는 게 여러 개임을 인식하게 해 준다.
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)
3) PUT, DELETE
PUT은 데이터를 수정하는 것을 말하며 UPDATE에 해당한다.DELETE는 데이터를 삭제하는 것이다.views.py에서 @api_view()에 PUT과 DELETE를 추가해 준다.업데이트(UPDATE) 및 삭제(DELETE)를 위해서는 하나의 질문을 선택해 주어야 하므로 PK인 ID를 통해 수정 혹은 삭제할 질문을 가지고 올 수 있도록 한다.PUT의 경우 업데이트 로직이기 때문에 POST와 동일하게 유효성 검사를 해 준 후 유효한 데이터이면 .save() 아니라면 .errors를 통해 에러 메시지를 보여 준다.DELETE는 단순하게 삭제 기능이기 때문에 .delete()를 통해 삭제 처리만 해 주면 된다. 다만 삭제가 완료되었으므로 더 이상 반환할 데이터가 없다는 뜻에서 204_NO_CONTENT 코드를 사용한다.@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)
views.py를 수정해 줘야 하며 class로 구현할 경우 APIView를 사용하기 때문에 rest_framework.views의 APIView를 import 해 준다.if-else 조건문이 아니라 각각의 함수로 구현할 수 있어 코드가 잘 정리되고 반복되는 코드가 없다는 장점이 있다.from django.shortcuts import render, get_object_or_404
from polls.models import Question
from polls_api.serializers import QuestionSerializer
from rest_framework.response import Response
from rest_framework.decorators import api_view
from rest_framework import status
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)
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)
호출 url 부분이 수정되어야 하므로 urls.py를 수정해 준다.class_name.as_view()를 통해 호출해 주어야 한다.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'),
]
class 형식을 사용해 구현한다..list, 하나의 값을 가지고 올 때는 .retrieve, 새로 만들 때는 .create, 수정할 때는 .update, 마지막으로 삭제할 때는 .destroy를 쓰면 된다.from django.shortcuts import render, get_object_or_404
from polls.models import Question
from polls_api.serializers import QuestionSerializer
from rest_framework.response import Response
from rest_framework.decorators import api_view
from rest_framework import status, mixins, generics
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)
QuestionDetail을 호출할 때 pk인 id가 필요한데 이는 url을 통해서 넘겨 줘야 하므로 urls.py를 수정한다.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'),
]
get, put, delete를 하지 않아도 RetrieveUpdateDestroyAPIView나 ListCreateAPIView 내부에 모두 구현되어 있기 때문이다.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
create에 해당)update에 해당)Water-Fall 모델은 소프트웨어 개발에 부적합하다.Water-Fall 모델: 요구 조건 -> 디자인 -> 개발 -> 테스트 -> 릴리스 -> 유지 보수애자일 개발(Agile Development) 스프린트(Sprint)라고 부르는데 이 사이클을 단기간(보통 2주)에 반복적으로 도는 방식이다.우선 순위 결정 (Backlog Prioritization)PM이 수행Grooming이라고 부름중요도와 복잡도를 결정Standup 미팅 (5 분에서 10 분)Retrospective(회고: 좋았던 점, 아쉬웠던 점) & Demo(데모: 좀 더 시각화된 형식으로 결과를 보여 줌) 미팅스프린트 카드(Task)에는 어떤 내용이 들어갈까? 플랜닝 포커(Planning Poker) 애자일 개발의 원칙에 어긋나게 된다.애자일 개발의 원칙에 어긋나게 된다. 가볍게 진행되는 사항만 보고하고 문제가 발생할 경우 혹은 의논할 게 있는 경우 따로 미팅을 잡는다.JIRATrello: JIRA보다 훨씬 단순하다.CVS(Concurrent Version System)SVN(SubVersionN)Git/Github : 가장 인기 있는 버전 컨트롤 소프트웨어. 대부분의 오픈소스 소프트웨어들은 Github에 존재.코드 리뷰 통해 진행한다.STAGING SERVER와 같은 서버에 배포)Software Engineering Pratice의 하나1. OrderedDict
- 일반
Dictionary는 순서를 보장하지 않기 때문에 키-값의 쌍이 순서대로 유지되지 않는다.OrderedDict은 그 순서를 보장하는Dictionary이다.- 만약
OrderedDict.get('key', default처리-키가 없을 경우)랄 하면 value 값을return받을 수 있다.- 하지만
Python 3.7부터는Dictionary역시 키-값으로 이루어지고 순서를 보장한다고 선언되었다.
2.
serializer실행 시rest_framework 모듈이 없다는 오류 발생
- Django Shell에서 다음과 같은 명령어from polls_api.serializers import QuestionSerializer로 만든 serializer을 실행하려는 과정에서ModuleNotFoundError: No module named 'rest_framework'오류가 발생하였다.
- 발생한 까닭은rest_framework가 설치되어 있지 않기 때문이었다.
- 이 문제를 해결하기 위해서는python -m pip install djangorestframework다음과 같은 명령어로rest_framework를 설치해 주어야 했다.
3.
AssertionError: You must call '.is_valid()' before accessing '.validated_data'오류
- 해당 오류의 발생 원인은
serializer된 값을유효성 검사를 해 주지 않았기 때문에유효한 값인지를 알기 전에.save()나.validated_data를 처리할 수 없어 발생하는 오류이다.유효성 검사는.is_valid()를 통해 가능하며True일 경우 처리할 수 있는 유효한 데이터,False인 경우 유효하지 않은 데이터라는 뜻이다.
4. Django .
TemplateDoesNotExist at /rest/question/오류
- 📑Django API 기본 페이지 표출 시 오류
- 해결 과정을 포스팅 해 두었다.
5. HTTP Status Code
- 200 번대: 정상적인 결과
- 200 OK
- 201 CREATED
- 400 번대: 사용자의 잘못된 요청
- 400 BAD REQUEST
- 404 NOT FOUND
- 500 번대: 서버 내부 오류