본문 바로가기
python

django rest framework의 serializer, view 추상화 과정

by 초특급하품 2020. 6. 25.

간단한 User 모델과 Serializer를 정의하고 추상화를 통해 반복되는 코드를 줄여보며 누가 마법을 부리는지 확인해보자.

 

user/models.py

GENDERS = [
  ('M', 'Male'),
  ('F', 'Female'),
]

class User(models.Model):

    name = models.CharField(blank=True, max_length=255)
    gender = models.CharField(blank=True, choices=GENDERS, max_length=1)
    created = models.DateTimeField(auto_now_add=True)

 

user/serializers.py

class UserSerializer(serializers.Serializer):
    pk = serializers.IntegerField(read_only=True)
    name = serializers.CharField()
    gender = serializers.ChoiceField(choices=GENDERS)
    created = serializers.DateTimeField()

 

python manage.py shell로 모델을 직접 생성하고 직렬화가 잘 되는지 테스트할 수 있다.

from user.models import User
from user.serializers import UserSerializer

user = User(name='myname', gender='M')
user.save()

serializer = UserSerializer(user)
serializer.data
> {'pk': 1, 'name': 'myname', 'gender': 'M', 'created': '2020-06-24T11:56:51.078453Z'}


serializers = UserSerializer(User.objects.all(), many=True)
serializers.data
> [OrderedDict([('pk', 1), ('name', 'myname'), ('gender', 'M'), ('created', '2020-06-24T11:56:51.078453Z')])]

 

serializer를 구현할 때 model에서 정의한 타입을 반복하고 있는데, ModelSerializer를 상속받으면 간단해진다.

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('id', 'name', 'gender', 'created')

 

 


 

이번에는 view로 넘어간다.

APIView를 상속하면 http method 단위로 함수를 정의하기만 하면 된다. 페이징이나 예외 처리는 일단 접어두고 전체 유저를 조회하는 class와 특정 유저를 조회하는 class를 만든다.

class UserList(APIView):
    def get(self, request):
        users = User.objects.all()
        serializer = UserSerializer(users, many=True)
        return Response(serializer.data)

    def post(self, request):
        serializer = UserSerializer(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 UserDetail(APIView):
    def get(self, request, pk):
        user = User.objects.get(pk=pk)
        serializer = UserSerializer(user)
        return Response(serializer.data)

 

url path를 고려해서 유저를 생성하는 post api도 UserList class 안에 넣어두었다.

다음으로 user/urls.py에 위에서 만든 class 기반의 view를 as_view()로 연결시켜준다.

urlpatterns = [
    url(r'^users$', UserList.as_view()),
    url(r'^users/(?P<pk>[0-9]+)$', UserDetail.as_view()),
]

 

python manage.py runserver로 기동 후 api 요청을 날려보면 응답이 정상적으로 온다.

$ curl -XGET http://127.0.0.1:8000/users
[{"id":1,"name":"myname","gender":"M","created":"2020-06-24T11:56:51.078453Z"},{"id":2,"name":"myname-1","gender":"M","created":"2020-06-24T12:09:17.135891Z"},{"id":3,"name":"myname","gender":"M","created":"2020-06-24T11:59:18.041580Z"}]

$ curl -XGET http://127.0.0.1:8000/users/1
{"id":1,"name":"myname","gender":"M","created":"2020-06-24T11:56:51.078453Z"}

$ curl -XPOST http://127.0.0.1:8000/users -H 'content-type: application/json' -d '{"name": "new name", "gender": "F" }'
{"id":4,"name":"new name","gender":"F","created":"2020-06-24T12:04:25.814279Z"}

 

이렇게 해도 잘 동작하지만 views.py의 코드를 더 추상화할 수 있다.
APIView를 지우고 generics.GenericAPIView과 mixin을 조합하면 더욱 간결해진다.

class UserList(generics.GenericAPIView,
               mixins.ListModelMixin,
               mixins.CreateModelMixin,
 ):
    queryset = User.objects.all()
    serializer_class = UserSerializer

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

 

요구되는 attribute, method만 구현하면 GenericAPIView가 마법같이 모든 걸 처리해준다.

  1. queryset: 해당 class view에서 사용되는 queryset
  2. serializer_class: 해당 class view에서 validation, de/serialize에 사용되는 serialize class

이 두 attribute는 method로 대체할 수도 있고, 이 값 외에도 많은 속성들이 있다. (https://www.django-rest-framework.org/api-guide/generic-views/)

 

GenericAPIView를 상속해서 필요한 속성들을 설정하면 mixin class가 queryset, serializer_class를 이용해서 적절한 http code로 응답한다. GenericAPIView와 mixin의 조합을 상속한 generics.ListCreateAPIView를 사용하면 유일한 중복 코드인 get, post method마저도 지울 수 있다.

class UserList(generics.ListCreateAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer

'python' 카테고리의 다른 글

django 커스텀 유저 모델  (0) 2022.02.01
django model 정리  (0) 2020.06.13
cookiecutter로 django 프로젝트 생성  (0) 2020.06.11
Python 개발 가상환경 설정  (0) 2019.10.20

댓글