간단한 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가 마법같이 모든 걸 처리해준다.
- queryset: 해당 class view에서 사용되는 queryset
- 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 |
댓글