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)
애자일 개발의 원칙에 어긋나게 된다.
애자일 개발의 원칙에 어긋나게 된다.
가볍게 진행되는 사항만 보고하고 문제가 발생할 경우 혹은 의논할 게 있는 경우 따로 미팅을 잡는다.JIRA
Trello
: 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 번대: 서버 내부 오류