-
[Django] DRF Router(라우터) 동작 원리파이썬/Django 2022. 2. 13. 22:42
들어가며
DRF의 대표 클래스(
ViewSet
,Serializer
,Router
)중 하나인Router
의 종류와 동작원리를 알아본다.Router
는 간단하고 빠르게 자동으로 url 라우팅을 할 수 있도록 해준다.from rest_framework import routers from apps.account. import views router = routers.SimpleRouter() router.register(r'users', views.UserViewSet) urlpatterns = router.urls
위와 같은 코드를 작성하면
^users/$
^users/{int: lookup}/$
의 url패턴이 자동으로 생성된다.
DRF에는
SimpleRouter
,DefaultRouter
두 가지를 제공한다.SimpleRouter
이 라우터를 사용하면 기본적으로 ViewSet에 있는
list
,create
,retrieve
,update
,partial_update
,destory
액션들에 대해서 라우팅을 해준다.❗ 액션이란 Viewset안에서 router를 통해 request를 처리할 수 있는 메서드이다. `엔드포인트`라고 알면 되겠다. 나중에 ViewSet의 classmethod를 동해 view로 바뀐다. (1 action = 1 view)
DRF 공식문서에서 제공하는 표이다.
URL Style HTTP Method Action URL Name {prefix}/ GET list {basename}-list POST create {prefix}/{url_path}/ GET, or as specified by methods argument @action(detail=False) decorated method {basename}-{url_name} {prefix}/{lookup}/ GET retrieve {basename}-detail PUT update PATCH partial_update DELETE destroy {prefix}/{lookup}/{url_path}/ GET, or as specified by methods argument @action(detail=True) decorated method {basename}-
이렇게 Router에 정의된
basename
,prefix
에 따라list
,create
,retrieve
,update
,partial_update
,destory
액션에 대해 라우팅이 자동으로 되며@action
데코레이터의 정의에 따라 라우팅이 이루어진다.DefaultRouter
위의
SimpleRouter
와 비슷하지만 추가로 자동으로root endpoint
를 생성한다.root endpoint
는 모든 뷰셋의list
action endpoint를 하이퍼링크 또는 json 형식으로 반환해준다.# views.py class UserAPIViewSet(GenericViewSet): ... def list(self, request): data = self.get_serializer(self.get_queryset(), many=True).data return Response(status=200, data=data) class ProfileAPIViewSet(GenericViewSet): ... def retrieve(self, request); data = self.get_serialzer(self.get_object()).data return Response(status=200, data=data) # urls.py app_name = 'api' router_v1 = routers.DefaultRouter(trailing_slash=False) router_v1.register('user', UserAPIViewSet, basename='user') router_v1.register('profile', ProfileAPIViewSet, basename='profile') urlpatterns = [ path('v1/', include(router_v1.urls)) ]
위와 같이 정의되어 있을 경우
https://www.domain.com/api/v1/
에 요청할 경우 response는{ 'user': "https://www.domain.com/api/v1/user" }
이 된다.
ProfileAPIViewSet
에는list
action이 없으므로 포함되지 않는다.Router 동작 원리
router가 사용되는 코드를 보면서 동작원리를 알아보면
# 1. router_v1 = routers.SimpleRouter(trailing_slash=False) # 2. router_v1.register('user', UserAPIViewSet, basename='user') router_v1.register('profile', ProfileAPIViewSet, basename='profile') # 3. urlpatterns = [ path('v1/', include(router_v1.urls)) ]
1. SimpleRouter 객체를 생성한다.
2.
register
메서드를 사용해서 viewset을 등록한다.registry
라는 리스트 애트리뷰트에 (prefix, viewset, basename) 순으로 tuple형태로 추가한다.3. urls 프로퍼티 접근 시
registry
를 사용해서 URL을 자동으로 생성한다.이 부분을 더 자세하게 로직을 들여다 보면
3-1. 2번에서 등록한 registry를 iterate하면서 viewset마다 route들을 가져온다.
아래는 route들을 가져오는 로직이다.
def get_routes(self, viewset): # 1) known_actions = list(flatten([route.mapping.values() for route in self.routes if isinstance(route, Route)])) # 2) extra_actions = viewset.get_extra_actions() # 3) not_allowed = [ action.__name__ for action in extra_actions if action.__name__ in known_actions ] if not_allowed: msg = ('Cannot use the @action decorator on the following ' 'methods, as they are existing routes: %s') raise ImproperlyConfigured(msg % ', '.join(not_allowed)) # 4) detail_actions = [action for action in extra_actions if action.detail] list_actions = [action for action in extra_actions if not action.detail] routes = [] # 5) for route in self.routes: if isinstance(route, DynamicRoute) and route.detail: routes += [self._get_dynamic_route(route, action) for action in detail_actions] elif isinstance(route, DynamicRoute) and not route.detail: routes += [self._get_dynamic_route(route, action) for action in list_actions] else: routes.append(route) return routes """ 1) 기본으로 라우터에 정의되어 있는list, create, retrieve, update, partial_update, destory 액션들을 가져온다. (3번에서 체크를 하기위해) 2) viewset에 @action 데코레이터가 적용된 메서드들을 가져온다. → extra_actions 3) extra_actions들 중에 기본으로 라우터가 제공하는 동일한 이름의 action이 있는지 체크한다. 4) extra_actions 를 detail에 따라 나눈다.(detail=True면 prefix뒤에 lookup(보통 pk)이 붙는다.) 5) 기본으로 라우터에 정의되어 있는 경로들 + detail=True인 경로 + detail=False인 경로들을 모두 리스트에 넣어 반환한다. """
3-2 가져온 route들을 viewset의 클래스 메서드인 as_view를 사용해서 각각의 view를 만들어준다.
3-3 마지막으로, 만든 view를 사용해 regex path를 만들어 결과 리스트에 append해준다.
마치며
DRF의 라우터의 종류 및 동작원리를 알아보았다. 각 action은 view로 바뀌게 된다. SimpleRouter, DefaultRouter의 차이점은 크게 다르지 않다. 단지, root endpoint를 생성해주냐 안해주냐의 차이다.
'파이썬 > Django' 카테고리의 다른 글
[Django] DRF Serializer(시리얼라이저) 동작 원리 (0) 2022.01.24 pydantic이란? (DRF Serializer를 대체할 수 있을까?) (1) 2021.12.14 Docker + Django에 Gitlab-CI(Pytest) 붙이기 (0) 2021.10.24 [Django] Django의 동작 원리와 과정 파헤치기 (1) 2021.10.20 Gunicorn 동작 과정 알아보기 (0) 2021.10.10