DRF 코드 이해

Joey Lee·2020년 7월 14일
7

Django REST Framework

목록 보기
15/16

1. CustomerSerializers.py 코드 리뷰

class CustomerSerializer(serializers.ModelSerializer):
    team = serializers.StringRelatedField(read_only=True)
    country = serializers.PrimaryKeyRelatedField(queryset=Country.objects.all())

    class Meta:
        model = Customer
        fields = "__all__"

2. DetailAPIView 코드 리뷰

1) views.py 코드

class CustomerDetailAPIView(generics.RetrieveUpdateDestroyAPIView):
    queryset = Customer.objects.all()
    serializer_class = CustomerSerializer
    permission_classes = [IsAuthenticated]
  • DetailAPIView인데 왜 queryset은 Customer.objects.all()인가?
  • 코드는 아주 심플한테, 어떻게 이 3줄의 코드가 detail, update, delete 기능을 수행하는가?
    ==> 위의 코드가 어떻게 작동하는지 이해할려면, get_object 메소드의 역할에 대한 이해가 중요함

2) RetrieveUpdateDestroyAPIView 소스 코드

class RetrieveUpdateDestroyAPIView(mixins.RetrieveModelMixin,
                                   mixins.UpdateModelMixin,
                                   mixins.DestroyModelMixin,
                                   GenericAPIView):
    """
    Concrete view for retrieving, updating or deleting a model instance.
    """
    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 patch(self, request, *args, **kwargs):
        return self.partial_update(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

3) RetrieveMixin 소스 코드

3-1) 코드 보기

get_object는

  • queryset과 pk값을 인자로 받아서,
  • queryset.filter(pk=pk)로 queryset을 뽑고,
  • obj = queryset.get()으로 객체만 뽑아서 리턴해 주는 메소드임
    => 결국, 위 코드는 Customer.objects.get(pk=pk) 리턴함

get_serializer는

  • instance을 인자로 받아서, 해당 객체을 serialize해 주는 메소드임
# RetrieveModelMixin 소스 코드

class RetrieveModelMixin:
    """
    Retrieve a model instance.
    """
    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()   #  Customer.objects.get(pk=pk)를 의미
        serializer = self.get_serializer(instance)
        return Response(serializer.data)        

# get_object 소스 코드

def get_object(self, queryset=None):
        """
        Returns the object the view is displaying.

        By default this requires `self.queryset` and a `pk` or `slug` argument
        in the URLconf, but subclasses can override this to return any object.
        """
        # Use a custom queryset if provided; this is required for subclasses
        # like DateDetailView
        if queryset is None:
            queryset = self.get_queryset()

        # Next, try looking up by primary key.
        pk = self.kwargs.get(self.pk_url_kwarg, None)
        slug = self.kwargs.get(self.slug_url_kwarg, None)
        if pk is not None:
            queryset = queryset.filter(pk=pk)

        # Next, try looking up by slug.
        if slug is not None and (pk is None or self.query_pk_and_slug):
            slug_field = self.get_slug_field()
            queryset = queryset.filter(**{slug_field: slug})

        # If none of those are defined, it's an error.
        if pk is None and slug is None:
            raise AttributeError("Generic detail view %s must be called with "
                                 "either an object pk or a slug."
                                 % self.__class__.__name__)

        try:
            # Get the single item from the filtered queryset
            obj = queryset.get()
        except queryset.model.DoesNotExist:
            raise Http404(_("No %(verbose_name)s found matching the query") %
                          {'verbose_name': queryset.model._meta.verbose_name})
        return obj   # Customer.object.get(pk=pk) 의미

# get_serializer 소스 코드

 def get_serializer(self, instance=None, data=None,
                       files=None, partial=False):
        """
        Return the serializer instance that should be used for validating and
        deserializing input, and for serializing output.
        """
        serializer_class = self.get_serializer_class()
        context = self.get_serializer_context()
        return serializer_class(instance, data=data, files=files,
                                partial=partial, context=context)
3-2) Python Shell 확인
from contacts.models import *

> queryset = Customer.objects.all()
> guery = queryset.filter(id=7)
<QuerySet [<Customer: Tecnituberias>]>

> obj = guery.get()
> obj
<Customer: Tecnituberias>
3-3) 테스트 확인

2) UpdateModelMixin

소스코드를 보기 전에 Django REST 공식문서 사례를 통해서 아래 내용을 먼저 숙지할 필요가 있다.

class UpdateModelMixin:
    """
    Update a model instance.
    """
    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)

        if getattr(instance, '_prefetched_objects_cache', None):
            # If 'prefetch_related' has been applied to a queryset, we need to
            # forcibly invalidate the prefetch cache on the instance.
            instance._prefetched_objects_cache = {}

        return Response(serializer.data)

    def perform_update(self, serializer):
        serializer.save()

    def partial_update(self, request, *args, **kwargs):
        kwargs['partial'] = True
        return self.update(request, *args, **kwargs)

3. ListCreateView 이해

1) 코드

  • get_queryset은 왜 override하여 작성하였는가?
    => Customer.objects.filter(team=team)으로 해야 하기 때문임. 만약, all() 혹은 filter(pk=pk)였다면 굳이 override할 필요가 없음
  • serialzer.py에서는 create를 왜 override하여 작성하였는가?
    => DB에 저장해야 될 모델이 Board뿐만 아니라 BoardAccess도 있기 때문에 둘을 동시에 같이 저장하기 위해서 create을 override하여 작성함
  • perform_create는 왜 overried하여 작성하였는가?
    => request로 전달된 데이터 외에 팀 정보도 추가해서 저장해야 하기 때문에 override하여, serializer.save(team=team)으로 저장을 함
class CustomerListCreateAPIView(generics.ListCreateAPIView):
    serializer_class = CustomerSerializer
    permission_classes = [IsAuthenticated]

    def get_queryset(self):
        team = self.request.user.profile.team
        queryset = Customer.objects.select_related('team').filter(team=team)
        return queryset
    
    def perform_create(self, serializer):
        team = self.request.user.profile.team
        serializer.save(team=team)

2) Mixin 소스코드

2-1) CreateModelMixin 이해하기

[create 메소드 구동 플로우]

    1. request로 전달된 데이터를 serialize해서 serializer 변수에 담기
    1. .is_valid로 유효성 검사 진행 (오류 시, 에러 발생)
    1. .perform_create() 수행
      => serializer.save() 수행 -> 객체 정보를 DB에 저장함
      => 단, 이번 사례에서는 request로 전달된 데이터 외에 팀 정보도 추가해서 저장해야 하기 때문에 override하여, serializer.save(team=team)으로 저장을 함
    1. 성공시, 헤더로 전달할 값들을 headers에 담기
    1. Response로 data, headers, status_code 전송
class CreateModelMixin:
    """
    Create a model instance.
    """
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

    def perform_create(self, serializer):
        serializer.save()

    def get_success_headers(self, data):
        try:
            return {'Location': str(data[api_settings.URL_FIELD_NAME])}
        except (TypeError, KeyError):
            return {}

2-2) ListModelMixin 이해하기

queryset = self.filter_queryset(self.get_queryset()) 이 부분이 잘 이해가 안 됨??


class ListModelMixin:
    """
    List a queryset.
    """
    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)
profile
안녕하세요!

2개의 댓글

comment-user-thumbnail
2020년 11월 24일

filter_queryset는 필터 처리하는 부분이에요.
generics.py에 보면 filter_queryset 함수 있습니다.

drf에서 자동으로 필터 처리 해주는 함수라고 생각하시면될것 같아요.
기본 필터 처리 함수요~

답글 달기
comment-user-thumbnail
2021년 3월 6일

많은 도움이 되었습니다! 감사합니다!

답글 달기