Miner

DAY 13 본문

데브코스

DAY 13

MineTime76 2023. 11. 1. 20:45

11/1 - 파이썬 장고 프레임워크를 사용해서 API 서버 만들기

 

1. serializer 시리얼라이져

  • Serialize : 모델 인스턴스나 QuerySet과 같은 데이터를 JSON 형식의 파일로 변환하는 작업
  • Deserialize : JSON 형식의 데이터를 정의된 포맷에 맞추어 다시 모델 인스턴스로 변환하는 작업

Serializer 는 Serialize와 Deserialize 를 모두 수행한다. Restful API 에서 데이터를 전송할 때 자주 사용된다. 

 

  1. 장고 restframework 기능을 수행할 새로운 app을 만든다.
python manage.py startapp poll_api

 

※ restframework 가 설치가 안되있다..! ---> 중간에 설치해서 들어가는 방법이 있지 않은 듯하다(망함)

 

2. 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):
        return Question.objects.create(**validated_data)

    def update(self, instance, validated_data):
        instance.question_text = validated_data.get('question_text', instance.question_text)
        instance.save()
        return instance

 

※ Ordereddict 사용

# dict
d = {'a': 1, 'b': 2, 'c': 3}
d['d'] = 4
d['e'] = 5
print(d)  # 예상 출력: {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}

 

주 특징 : 들어온 순서를 기억한다

2. Django Shell에서 Serializer 사용하기

  • 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):
        return Question.objects.create(**validated_data)

    def update(self, instance, validated_data):
        instance.question_text = validated_data.get('question_text', instance.question_text) + '[시리얼라이저에서 업데이트]'
        instance.save()
        return instance

 

  • Django Shell
#Serialize
>>> from polls.models import Question
>>> from polls_api.serializers import QuestionSerializer
>>> q = Question.objects.first()
>>> q 
<Question: 제목: 휴가를 어디서 보내고 싶으세요?, 날짜: 2023-02-05 18:52:59+09:00>
>>> serializer = QuestionSerializer(q)
>>> serializer.data
{'id': 1, 'question_text': '휴가를 어디서 보내고 싶으세요?', 'pub_date': '2023-02-05T18:52:59Z'}
>>> 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-02-05T18:52:59Z"}'

# Deserialize
>>> import json
>>> data = json.loads(json_str)
>>> data
{'id': 1, 'question_text': '휴가를 어디서 보내고 싶으세요?', 'pub_date': '2023-02-05T18:52:59Z'}
>>> serializer = QuestionSerializer(data=data)
>>> serializer.is_valid()  
True
>>> serializer.validated_data 
OrderedDict([('question_text', '휴가를 어디서 보내고 싶으세요?')])
>>> new_question = serializer.save() # Create
>>> new_question
<Question: 제목: 휴가를 어디서 보내고 싶으세요?, 날짜: 2023-02-14 18:46:56.209215+00:00>
>>> data={'question_text': '제목수정'}
>>> serializer = QuestionSerializer(new_question, data=data) 
>>> serializer.is_valid()  
True
>>> serializer.save() # Update
<Question: 제목: 제목수정[시리얼라이저에서 업데이트], 날짜: 2023-04-25 13:15:05.852404+00:00>

#Validation이 통과하지 않는 경우
>>> 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')]}

 

3. 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(read_only=True)
    question_text = CharField(max_length=200)
    pub_date = DateTimeField(read_only=True)
>>> serializer = QuestionSerializer(data={'question_text':'모델시리얼라이저로 만들어 봅니다.'})
>>> serializer.is_valid()
True
>>> serializer.save()
<Question: 제목: 모델시리얼라이저로 만들어 봅니다., 날짜: 2023-02-14 19:41:081444+00:00>

 

4. Get

 

정보를 획득할 때 - get

정보를 서버에 써달라고 할 때 - post

 

  • polls_api/views.py
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()
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
from django.urls import include, path
from django.contrib import admin

urlpatterns = [
    path('admin/', admin.site.urls),
    path('polls/', include('polls.urls')),
    path('rest/', include('polls_api.urls')),
]

 

5. HTTP Methods

(Create : 만들기) 에 해당하는 메소드 : POST(특정 리소스에 엔티티를 제출할 때, 이는 종종 서버의 상태의 변화나 부작용을 일으킨다.)

(Read : 읽기) 에 해당하는 메소드 : GET(특정 리소스의 표시를 요청한다, 오직 데이터를 받기만 한다.)

(Update : 수정하기) 에 해당하는 메소드 : PUT(목적 리소스 모든 현재 표시를 요청 payload로 바꾼다)

(Delete : 삭제하기) 에 해당하는 메소드 : DELETE(특정 리소스를 삭제한다.)

 

6. POST

@api_view() 에 아무것도 넣어주지 않으면 GET이 들어간다.

 

HTTP Status Code 

  • 200번대 : 정상적인 결과
    • - 200  OK
    • - 201  CREATED
  • 400번대 : 사용자의 잘못된 요청
    • - 400 BAD REQUEST
    • - 404 NOT FOUND
  • 500번대 : 서버 내부의 오류
  • polls_api/views.py
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

@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)

 

7. PUT / DELETE

  • views.py
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)
  • polls_api/urls.py
from django.urls import path
from .views import *

urlpatterns = [
    path('question/', question_list, name='question-list'),
    path('question/<int:id>/', question_detail, name='question-detail')
]

 

8. Class 기반의 뷰

  • polls_api/views.py
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)
  • 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'),
]

 

9. Mixin

class 기반으로 작성한 기능을 더 간단히 해주는 역할

 

  • polls_api/views.py
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)
  • polls_api/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'),
]

 

10. Generic API View

  • polls_api/views.py
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

 

'데브코스' 카테고리의 다른 글

6주차 -1  (0) 2023.11.11
DAY 14  (0) 2023.11.02
DAY 12  (1) 2023.10.31
DAY 11  (0) 2023.10.30
DAY 09  (0) 2023.10.26