APIView 사용해보기 & Serializer

0

장고 프로젝트

목록 보기
15/15
post-thumbnail

APIView 사용하기

어제 정리한 APIView를 직접 사용해보려고 한다.

model은 이전 프로젝트에서 작성해둔 것을 쓸 것이다.

# user/models.py
from django.db import models
from django.contrib.auth.models import AbstractUser, PermissionsMixin


# AbstractUser 상속 / setting.py에 AUTH_USER_MODEL = '앱이름.클래스이름' 써줘야 한다
class User(AbstractUser, PermissionsMixin):
    class Meta:
        ordering = ['date_joined']


# 이두 운동 관련된 모델
class BicepsCurl(models.Model):
    username = models.CharField(max_length=25)

    count = models.IntegerField()
    count1 = models.IntegerField()
    count2 = models.IntegerField()

    sum_times = models.IntegerField(null=True)
    sum_count = models.IntegerField(null=True)

    times = models.FloatField(max_length=255)
    title = models.CharField(max_length=255, null=True)
    day = models.CharField(max_length=255, null=True)
    created = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return ("TotalCount:"+str(self.count)+" LCount:"+str(self.count1)+
                " RCount:"+str(self.count2)+" time:"+str(self.times)+" day:"+self.day)

    class Meta:
        ordering = ['created']


# 스쿼트 운동 관련 모델
class Squat(models.Model):
    username = models.CharField(max_length=25)

    count = models.FloatField(max_length=255)

    sum_times = models.IntegerField(null=True)
    sum_count = models.IntegerField(null=True)

    times = models.FloatField(max_length=255)
    title = models.CharField(max_length=255, null=True)
    day = models.CharField(max_length=255, null=True)
    created = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return "Count:"+str(self.count)+" Time:"+str(self.times)+" Date:"+self.day

    class Meta:
        ordering = ['created']


# 푸쉬업 운동 관련 모델
class PushUp(models.Model):
    username = models.CharField(max_length=25)

    count = models.FloatField(max_length=255)

    sum_times = models.IntegerField(null=True)
    sum_count = models.IntegerField(null=True)

    times = models.FloatField(max_length=255)
    title = models.CharField(max_length=255, null=True)
    day = models.CharField(max_length=255, null=True)
    created = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return "Count:"+str(self.count)+" Time:"+str(self.times)+" Date:"+self.day

    class Meta:
        ordering = ['created']

사실상 모든 운동 모델이 거의 동일하다.
사용자 이름 / 횟수 / 운동 시간 / 요일 / 날짜 등을 받아서 정리한다.

모든 요청에는 insomnia REST Client를 사용할 것이다.

# user/views.py: Class Based View 작성하기

# 이두 운동 데이터에 대해
class BicepsListAPIView(APIView):
    # get 요청이 들어왔을 때
    def get(self, request):
        # BicepsCurl 모델의 데이터를 직렬화 / 필드값이 복수일 때 many=Ture
        serializer = BicepsSerializer(BicepsCurl.objects.all(), many=True)
        # 직렬화한 데이터를 반환
        return Response(serializer.data)
        
# user/urls.py: 위에 작성한 View에 url 붙여주기
    ...
    path('BicepsListAPIView/', BicepsListAPIView.as_view())
    ...

이렇게 해놓고 해당 주소로 get요청을 보내면
이렇게 저장해둔 기록들이 주르륵 잘 나오는 것을 볼 수 있다.
혹시 get요청을 보내면서 body부분에 'username'을 첨부하면 해당 사용자의 기록을 볼 수 있을까?

# user/views.py: 이전 코드를 조금만 수정

# 이두 운동 데이터에 대해
class BicepsListAPIView(APIView):
    # get 요청이 들어왔을 때
    def get(self, request):
        # request의 body에서 username에 해당하는 데이터를 get_username 변수에 할당
        get_username = request.data['username']

        # BicepsCurl 모델의 데이터를 직렬화 / 필드값이 복수일 때 many=Ture
        # serializer = BicepsSerializer(BicepsCurl.objects.all(), many=True)

        # get_username과 일치하는 username의 BicepsCurl 기록을 직렬화
        serializer = BicepsSerializer(BicepsCurl.objects.filter(username=get_username), many=True)
        # 직렬화한 데이터를 반환
        return Response(serializer.data)

이렇게 코드를 바꾸고 get요청을 하면서 body에 'username'을 넣으면?
된다...🤔 이야.. 좋은 것 같다!
이게 안 돌아갔으면 FBV로 돌아갔어야 했는데 아주 잘 처리가 된다.

이렇게 되면 post 멤버 함수에는 기록 생성 기능을 넣을 수 있게 됐다.

def post(self, request): 
    ... 	

위와 같은 함수를 class BicepsListAPIView(APIView):의 멤버함수로 작성해줄 것인데, 예제 코드를 확인해보면 .is_valid(): 메서드를 흔하게 볼 수 있다. 어설프게나마 완료한 프로젝트에서도 이 메서드를 사용하긴 했지만 제대로 알고 넘어간다면 도움이 될 것 같다.

그러므로 살펴본다. django serializer부터. 초심초심.

django serializer

지금부터의 내용은 다음의 링크들을 보고 공부하며 작성한다.

두 분 모두 공식문서를 참고하여 작성해주셨다.
이하 내용은 아마도 거의 복붙 수준일 것이다..

미리 감사의 인사 드립니다.
감사한 마음으로 받아 먹겠습니다. 🙇🏻‍♂️

직렬화란?

모든 프로그래밍 언어의 통신에서, 데이터는 반드시 문자열로 표현되어야만 한다.

  • 송신자: 객체를 문자열로 변환하여 전송
    이 과정을 직렬화라고 한다.

  • 수신자: 수신한 문자열을 다시 객체로 변환하고 이를 활용한다
    이 과정을 비직렬화라고 한다.

비직렬화는 수신한 문자열을 다시 객체로 변환하고 이를 활용한다고 되어 있다.
이 과정은 다음과 같이 정리할 수 있다.

  1. 데이터를 받는다
  2. parse를 사용하여 Python자료형으로 변환
  3. validator를 사용하여 유효성 검증
  4. parsing된 데이터를 다시 객체화

대표적인 직렬화 포맷에는 JSON, XML이 있다.
JSON에 대해서도 간단히 살펴봐야겠다.

JSON

자료 출처: MDN문서

JSON이란?

JavaScript Object Notation.

  • JavaScript 객체 문법으로 구조화된 데이터를 표현하기 위한 문자 기반의 표준 포맷.
  • 웹 어플리케이션에서 데이터를 전송할 때 일반적으로 사용.

JSON은 문자열 형태로 존재한다.
따라서 네트워크를 통해 전송할 때 아주 유용하다.

🔎 문자열을 네이티브 객체로 변환하는 것을 파싱(Parsing)이라고 한다.
네트워크를 통해 전달할 수 있도록 객체를 문자로 변환하는 과정은 문자열화(Stringification)이라고 한다.

개별 JSON 객체를 .json 확장자의 단순 텍스트 파일에 저장할 수 있다.
MIME 타입은 application/json이다.

JSON의 구조

JavaScript 객체 리터럴 문법을 따르는 문자열이다.
따라서 JavaScript의 기본 데이터 타입인 문자열, 숫자, 배열, 참과 거짓 그리고 기타 객체를 포함할 수 있다.

{
  "squadName": "Super hero squad",
  "homeTown": "Metro City",
  "formed": 2016,
  "secretBase": "Super tower",
  "active": true,
  "members": [
    {
      "name": "Molecule Man",
      "age": 29,
      "secretIdentity": "Dan Jukes",
      "powers": [
        "Radiation resistance",
        "Turning tiny",
        "Radiation blast"
      ]
    },
    {
      "name": "Madame Uppercut",
      "age": 39,
      "secretIdentity": "Jane Wilson",
      "powers": [
        "Million tonne punch",
        "Damage resistance",
        "Superhuman reflexes"
      ]
    },
    {
      "name": "Eternal Flame",
      "age": 1000000,
      "secretIdentity": "Unknown",
      "powers": [
        "Immortality",
        "Heat Immunity",
        "Inferno",
        "Teleportation",
        "Interdimensional travel"
      ]
    }
  ]
}

기본적으로 중괄호{}안에 쌍따옴표("")와 콤마(,) 그리고 대괄호[]로 구성된다.
CDN 문서에 너무나 기막히게 잘 설명되어 있는데, 이건 한번 따라해볼 만한 것 같다.

validator

권태형 개발 블로그: [해설과 함께 읽는 DRF 문서] Validation에 기가막힌 설명글이 있다.
감사합니다. 🙇🏻‍♂️

"데이터를 역직렬화 할 때, 유효성이 보장된 데이터에 접근 하거나, 객체 인스턴스를 저장하기 전에 is_valid() 호출하는 것은 필수이다"라는 설명을 볼 수 있다.

앞서 .is_valid()는 유효성 검증을 위한 과정이라고 했다.

결론부터 말하자면 serializer.data에 접근하기 위해서는 반드시 .is_valid()가 선행되어야 한다.

이는 흔히 create 혹은 update 동작에 대해서 외부로 부터 전해져온 데이터의 유효성 검사가 완료되어야만 데이터를 생성하고 업데이트를 하는 것과 같은 맥락에서 이해할 수 있다.

이만큼 정리했으니 다시 APIView를 짜볼 것이다.
알고 하는 거랑 모르고 하는 건 천지차이 같다.. 🤔

APIView 멤버 함수 마무리하기

글의 초반부에 이미

class CBV(APIView):
    def get(self, request):
        ...

이렇게 APIView를 상속받은 CBV를 작성해보았고, get요청에 body를 첨부해도 django는 알아서 잘 처리해준다는 것을 볼 수 있었다.

Unity 및 이에 사용되는 C# 스크립트를 메인으로 사용하는 프로젝트를 진행하면서, Http Request를 사용해봤는데 생각보다 안 되는 것이 많았다. get요청에 body를 넣으면 에러가 난다든지.. 물론 HttpWebRequest와 같은 클래스를 사용하면 또 에러가 나지 않았다. 자동으로 처리해주기 때문에..

아무튼 APIView를 상속하는 것임에도 이런 디테일한 자유도가 있다는 건 좋은 것 같다.

genericAPIView를 사용하여 아주 간편하게 처리할 수 있는 기능은 추후에 다뤄보는 것으로 하고, FBV@데코레이터들을 섞어 쓸 수 있는 방법을 배워보고 싶다.

일일이 뭔가 설정해줘야 하는 것은 다소 불편함이 있을 수 있지만, 인증이나 권한에 관한 최소한의 상속을 받으면서 최대한 자유롭게 코드를 짤 수 있는 방향에 흥미가 가는 듯하다.

다음은 @데코레이터를 살펴보겠다.


도움주신 귀한 분들 다시 한 번 감사합니다. 🙇🏻‍♂️

1개의 댓글

comment-user-thumbnail
2023년 8월 30일

감사합니다.

답글 달기