[TIL] 파이썬 장고 프레임웍을 사용해서 API 서버 만들기 (3)

이원진·2023년 4월 26일
0

데브코스

목록 보기
13/54
post-thumbnail
post-custom-banner

학습내용


  1. Serializer

  2. Django Shell에서 Serializer 사용하기

  3. ModelSerializer

  4. GET

  5. HTTP Methods

  6. POST

  7. PUT / DELETE

  8. Class 기반의 뷰

  9. Mixin

  10. Generic API View

1. Serializer


  • DRF(Django Rest Framework)에서 제공하는 기능

    • pip3 install djangorestframework

  • Serialize와 Deserialize의 기능

    • Serialize: 모델 인스턴스나 QuerySet과 같은 데이터를 JSON 형식의 파일로 변환하는 작업

    • Deserialize: JSON 형식의 데이터를 정의된 포맷에 맞춰 다시 모델 인스턴스로 변환하는 작업

  • OrderedDict: 저장된 key-value 쌍의 순서를 보장하는 딕셔너리

    • .get("requested_data", replace_data): 요청한 데이터가 있을 경우 해당 데이터를, 없을 경우 두 번째 인자로 주어지는 대체값을 반환

  • 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)
    
        # 유효성 검사를 통과한 JSON(OrderedDict) 형태의 데이터를 기반으로 생성
        def create(self, validated_data):
            return Question.objects.create(**validated_data)
    
        def update(self, instance, validated_data):
            # question_text라는 key가 없을 경우, 두 번째 인자로 주어지는 대체값을 사용
            instance.question_text = validated_data.get("question_text", instance.question_text)
            instance.save()
    
            return instance

2. Django Shell에서 Serializer 사용하기


  • Serialize

    • QuestionSerializer(question): Question 객체를 Serialize

    • JSONRenderer().render(serializer.data): Serialize된 데이터를 JSON 형식으로 렌더링

  • Deserialize

    • data = json.loads(json_str): JSON 형식의 데이터를 딕셔너리 형태로 Deserialize

    • serializer = Serializer(data = data)

    • serializer.is_valid(): 유효성 검사

    • serializer.save(): Serializer 객체의 create() 혹은 update() 메서드 호출


3. ModelSerializer


  • serializers.Serializer 대신 serializers.ModelSerializer를 상속해 더 간결하게 구현

  • polls_api/serializers.py

    ...
    
    class QuestionSerializer(serializers.ModelSerializer):
       # 메타 정보 기록
       class Meta:
           model = Question
           fields = ["id", "question_text", "pub_date"]
    • create(), update() 메서드도 정의하지 않아도 됨

4. GET


  • 정보를 조회하는 기능을 구현

  • project/urls.py

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

  • polls_api/urls.py

    from django.urls import path
    from .views import *
    
    urlpatterns = [
        path('question', question_list, name = "question-list"),
    ]

  • polls_api/views.py

    ...
    from rest_framework.decorators import api_view
    from rest_framework.response import Response
    from polls.models import Question
    from polls_api.serializers import QuestionSerializer
    
    # decorator를 사용해 GET 요청을 처리하는 api view임을 명시
    @api_view()
    def question_list(request):
        questions = Question.objects.all()
    
        # many 옵션을 통해 객체 리스트를 인자로 사용 가능
        serializer = QuestionSerializer(questions, many = True)
    
        return Response(serializer.data)

  • TemplateDoesNotExist 에러 발생

    • settings.py > INSTALLED_APPS에 'rest_framework' 추가해서 해결

5. HTTP Methods


  • 리소스: 기능 혹은 모델

  • CRUD에 대응

    • GET(Read): 특정 리소스 표시 요철

    • POST(Create): 특정 리소스에 엔티티를 제출

    • PUT(Update): 특정 리소스의 내용 수정

    • DELETE(Delete): 특정 리소스 삭제


6. POST


  • HTTP Status Code

    • 200번 대: 정상적인 결과
      • 200: OK
      • 201: CREATED

    • 400번 대: 사용자의 잘못된 요청
      • 400: BAD REQUEST
      • 404: NOT FOUND

  • polls_api/views.py

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


  • polls_api/views.py

    from django.shortcuts import render, 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)
    
        elif request.method == "PUT":
            serializer = QuestionSerializer(question, 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)
                
        elif request.method == "DELETE":
            question.delete()
    
            return Response(status = status.HTTP_204_NO_CONTENT)

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


  • Mixin 클래스를 상속해 기존과 같은 기능을 더 간결하게 구현

  • polls_api/views.py

    from rest_framework import status, mixins, 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

    ...
    
    urlpatterns = [
        ...
        path('question/<int:pk>/', QuestionDetail.as_view(), name = "question-detail"),
    ]
    • GenericAPIView는 id가 아닌 pk를 사용해 객체를 찾음

10. Generic API View


  • Generic 클래스들을 사용해 더욱 더 간결하게 구현

    from rest_framework import generics
    from polls.models import Question
    from polls_api.serializers import QuestionSerializer
    
    class QuestionList(generics.ListCreateAPIView):
        queryset = Question.objects.all()
        serializer_class = QuestionSerializer
    
    class QuestionDetail(generics.RetrieveUpdateDestroyAPIView):
        queryset = Question.objects.all()
        serializer_class = QuestionSerializer

메모



post-custom-banner

0개의 댓글