TIL

[DRF] APIView / GenericAPIView / Mixin / ViewSet

on 

๐Ÿš€ DRF์—์„œ๋Š” CBV๋ฅผ ์‚ฌ์šฉํ•œ ์˜ˆ์ œ๊ฐ€ ๋งŽ์•„ ์ƒˆ๋กœ์šด ๋ฐฉ์‹๋„ ๋ฐฐ์›Œ๋ณด๊ณ ์ž CBV๋ฅผ ๊ณต๋ถ€ํ•ด๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ๋น„์Šทํ•˜๋ฉด์„œ๋„ ์ฐจ์ด์ ์ด ๋ช…ํ™•ํ•ด๋ณด์—ฌ ์žŠ๊ธฐ ์ „์— ์ •๋ฆฌํ•ด๋ณด๋ฉด์„œ ๊ณต๋ถ€ํ•œ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค!

๊ณต์‹ ๋ฌธ์„œ

APIView

์ด ํด๋ž˜์Šค๋Š” Django์˜ ์ผ๋ฐ˜์ ์ธ View๋ฅผ ํ™•์žฅํ•˜์—ฌ API ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๋„๋ก ๋•์Šต๋‹ˆ๋‹ค. get(), post(), put(), delete() ๊ฐ™์€ HTTP ๋ฉ”์„œ๋“œ๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์ •์˜ํ•˜์—ฌ ์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

- ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ๋ทฐ ํด๋ž˜์Šค.
- HTTP ๋ฉ”์„œ๋“œ๋ฅผ ์ง์ ‘ ์ •์˜ํ•ด์•ผ ํ•จ.
- serializer, queryset ๋“ฑ์„ ์ˆ˜๋™์œผ๋กœ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•จ.
class MyAPIView(APIView):
    def get(self, request):
        # GET ์š”์ฒญ ์ฒ˜๋ฆฌ
    def post(self, request):
        # POST ์š”์ฒญ ์ฒ˜๋ฆฌ

GenericAPIView

GenericAPIView๋Š” APIView๋ฅผ ํ™•์žฅํ•œ ํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค. APIView๋ณด๋‹ค ์ข€ ๋” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋ฉฐ ์ง์ ‘์ ์œผ๋กœ CRUD ๊ธฐ๋Šฅ์„ ์ฒ˜๋ฆฌํ•˜์ง€๋Š” ์•Š์ง€๋งŒ, queryset๊ณผ serializer_class, lookup_field ๊ฐ™์€ ์†์„ฑ์„ ๊ธฐ๋ณธ์œผ๋กœ ์„ค์ •ํ•˜์—ฌ ๊ณตํ†ต ๋™์ž‘์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ์ข€ ๋” ์‰ฝ๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

- queryset, serializer_class ๋“ฑ์˜ ์†์„ฑ์„ ๊ธฐ๋ณธ์œผ๋กœ ์ œ๊ณต.
- get_object(), get_queryset(), get_serializer() ๋“ฑ์˜ ์œ ํ‹ธ๋ฆฌํ‹ฐ ๋ฉ”์„œ๋“œ๋ฅผ ์ œ๊ณตํ•˜์—ฌ ๊ฐ์ฒด ์กฐํšŒ์™€ ์ง๋ ฌํ™” ์ž‘์—…์„ ์‰ฝ๊ฒŒ ํ•  ์ˆ˜ ์žˆ์Œ.
- ๋” ๊ตฌ์ฒด์ ์ธ ๋ทฐ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋ฐ˜์„ ์ œ๊ณต.
class MyGenericAPIView(GenericAPIView):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer

    def get(self, request, *args, **kwargs):
        # GET ์š”์ฒญ ์ฒ˜๋ฆฌ

Mixin

mixin์€ GenericAPIView์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉฐ ๋” ๊ตฌ์ฒด์ ์ธ ๋™์ž‘์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. mixin๋Š” list, create, retrieve, update, delete ๊ฐ™์€ ๊ธฐ๋Šฅ์„ ๋”ฐ๋กœ ์ œ๊ณตํ•ด์ฃผ๋ฉฐ ์ด๋ฅผ ์กฐํ•ฉํ•˜์—ฌ ํ•„์š”์— ๋งž๋Š” ๋ทฐ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

class PostListMixins(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView):
	queryset = Post.objects.all()
	serializer_class = PostSerializer
    
	def get(self, request, *args, **kwargs):
		return self.list(request)
        
	def post(self, request, *args, **kwargs):
		return self.create(request)
    
class PostDetailMixins(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, generics.GenericAPIView):
	queryset = Post.objects.all()
	serializer_class = PostSerializer
    
	def get(self, request, *args, **kwargs):
		return self.retrieve(request)
        
	def put(self, request, *args, **kwargs):
		return self.update(request)
        
	def delete(self, request, *args, **kwargs):
		return self.delete(request)  

concrete generic view

DRF์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋ณธ์ ์ธ ์ œ๋„ค๋ฆญ ๋ทฐ(generic view)๋ฅผ ์ข€ ๋” ๊ตฌ์ฒดํ™”ํ•œ(view with specific behavior) ํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค. DRF๋Š” ์—ฌ๋Ÿฌ ๊ฐ€์ง€ CRUD ์ž‘์—…์„ ์‰ฝ๊ฒŒ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ฏธ๋ฆฌ ์ •์˜๋œ ๊ตฌ์ฒด์ ์ธ ์ œ๋„ค๋ฆญ ๋ทฐ๋“ค์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

class PostListGenericAPIView(generics.ListCreateAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

class PostDetailGenericAPIView(generics.RetrieveUpdateDestroyAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

ViewSet

ViewSet์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ์—ฌ๋Ÿฌ HTTP ๋ฉ”์„œ๋“œ(GET, POST, PUT, DELETE ๋“ฑ)์— ๋Œ€ํ•œ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ํ•˜๋‚˜์˜ ํด๋ž˜์Šค์—์„œ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„๋œ ํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค. ViewSet์„ ์‚ฌ์šฉํ•˜๋ฉด ๋” ๊ฐ„๊ฒฐํ•˜๊ณ  ๊ตฌ์กฐํ™”๋œ ์ฝ”๋“œ๋กœ CRUD API๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ViewSet

ViewSet์€ ์ผ๋ฐ˜์ ์ธ APIView์™€ ๋น„์Šทํ•˜์ง€๋งŒ, ์—ฌ๋Ÿฌ ๋™์ž‘์„ ํ•˜๋‚˜์˜ ํด๋ž˜์Šค์—์„œ ์ฒ˜๋ฆฌํ•˜๋„๋ก ์„ค๊ณ„๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์ธ APIView๊ฐ€ ๊ฐ ๋ฉ”์„œ๋“œ(get(), post() ๋“ฑ)๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋“œํ•˜๋Š” ๋ฐฉ์‹์ด๋ผ๋ฉด ViewSet์€ ๊ทธ ๋™์ž‘์— ๋งž๋Š” ํŠน๋ณ„ํ•œ ๋ฉ”์„œ๋“œ๋“ค(list(), retrieve(), create() ๋“ฑ)์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

์ฃผ์š” ๋ฉ”์„œ๋“œ

class MyViewSet(viewsets.ViewSet):
    def list(self, request):
        # ๋ฆฌ์ŠคํŠธ ์กฐํšŒ ๋กœ์ง
        queryset = User.objects.all()
        serializer = UserSerializer(queryset, many=True)
        return Response(serializer.data)
    def retrieve(self, request, pk=None):
        # ํŠน์ • ๊ฐ์ฒด ์กฐํšŒ ๋กœ์ง
        queryset = User.objects.all()
        user = get_object_or_404(queryset, pk=pk)
        serializer = UserSerializer(user)
        return Response(serializer.data)

GenericViewSet

Generic ViewSet์€ DRF์—์„œ ViewSet๊ณผ GenericAPIView์˜ ๊ธฐ๋Šฅ์„ ๊ฒฐํ•ฉํ•œ ํด๋ž˜์Šค๋กœ ๊ธฐ๋ณธ์ ์œผ๋กœ ViewSet์˜ ๊ตฌ์กฐ๋ฅผ ๋”ฐ๋ฅด๋ฉด์„œ๋„ ์ œ๋„ค๋ฆญ ๋ทฐ์˜ ์œ ์—ฐ์„ฑ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. GenericAPIView์˜ ๋ชจ๋“  ๊ธฐ๋Šฅ์„ ์ƒ์†๋ฐ›์œผ๋ฉด์„œ ViewSet์˜ ๋ผ์šฐํŒ… ๋ฐ ์ฒ˜๋ฆฌ ๊ธฐ๋Šฅ์„ ๊ฒฐํ•ฉํ•œ ๊ตฌ์กฐ๋ผ๊ณ  ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Generic ViewSet ์ž์ฒด๋Š” CRUD ์ž‘์—…์„ ์ง์ ‘ ์ œ๊ณตํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ํ•„์š”ํ•œ mixin์„ ๊ฒฐํ•ฉํ•˜์—ฌ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

class MyModelViewSet(mixins.CreateModelMixin,
                     mixins.RetrieveModelMixin,
                     mixins.UpdateModelMixin,
                     mixins.DestroyModelMixin,
                     mixins.ListModelMixin,
                     viewsets.GenericViewSet):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer

ModelViewSet

ModelViewSet์€ ViewSet์˜ ํ™•์žฅ์œผ๋กœ create(), retrieve(), update(), partial_update(), destroy(), list() ๋ฉ”์„œ๋“œ๊ฐ€ ์ž๋™์œผ๋กœ ๊ตฌํ˜„๋ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ชจ๋ธ๊ณผ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ์œผ๋ฉฐ queryset๊ณผ serializer_class๋ฅผ ์ง€์ •ํ•˜๋ฉด ์ž๋™์œผ๋กœ ๋ชจ๋“  CRUD ๋™์ž‘์ด ๊ตฌํ˜„๋ฉ๋‹ˆ๋‹ค.

class MyModelViewSet(viewsets.ModelViewSet):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer

ReadOnlyModelViewSet

ModelViewSet์˜ ์ฝ๊ธฐ ์ „์šฉ ๋ฒ„์ „์ž…๋‹ˆ๋‹ค. list()์™€ retrieve() ๋ฉ”์„œ๋“œ๋งŒ ์ œ๊ณตํ•˜๋ฉฐ CRUD ์ž‘์—…์€ ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

class MyReadOnlyModelViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer