11from importlib import import_module
2+ from types import ModuleType
23from django .conf import settings
34from django .core .urlresolvers import RegexURLResolver , RegexURLPattern
45from django .utils .module_loading import import_string
56from rest_framework .views import APIView
6- from rest_framework_docs .api_endpoint import ApiEndpoint
7+ from rest_framework .routers import BaseRouter
8+ from rest_framework_docs .api_endpoint import ApiNode , ApiEndpoint
9+ from rest_framework_docs .settings import DRFSettings
10+
11+
12+ drf_settings = DRFSettings ().settings
713
814
915class ApiDocumentation (object ):
1016
1117 def __init__ (self , drf_router = None ):
1218 self .endpoints = []
1319 self .drf_router = drf_router
20+
1421 try :
1522 root_urlconf = import_string (settings .ROOT_URLCONF )
1623 except ImportError :
@@ -21,26 +28,113 @@ def __init__(self, drf_router=None):
2128 else :
2229 self .get_all_view_names (root_urlconf .urlpatterns )
2330
24- def get_all_view_names (self , urlpatterns , parent_pattern = None ):
31+ def get_all_view_names (self , urlpatterns , parent_api_node = None ):
2532 for pattern in urlpatterns :
2633 if isinstance (pattern , RegexURLResolver ):
27- parent_pattern = None if pattern ._regex == "^" else pattern
28- self .get_all_view_names (urlpatterns = pattern .url_patterns , parent_pattern = parent_pattern )
29- elif isinstance (pattern , RegexURLPattern ) and self ._is_drf_view (pattern ) and not self ._is_format_endpoint (pattern ):
30- api_endpoint = ApiEndpoint (pattern , parent_pattern , self .drf_router )
34+ # Try to get router from settings, if no router is found,
35+ # Use the instance drf_router property.
36+ router = get_router (pattern )
37+ if router is None :
38+ parent_router = None
39+ if parent_api_node is not None :
40+ parent_router = parent_api_node .drf_router
41+ if parent_router is not None :
42+ router = parent_router
43+ else :
44+ router = self .drf_router
45+ if pattern ._regex == "^" :
46+ parent = parent_api_node
47+ else :
48+ parent = ApiNode (
49+ pattern ,
50+ parent_node = parent_api_node ,
51+ drf_router = router
52+ )
53+ self .get_all_view_names (urlpatterns = pattern .url_patterns , parent_api_node = parent )
54+ elif isinstance (pattern , RegexURLPattern ) and _is_drf_view (pattern ) and not _is_format_endpoint (pattern ):
55+ router = parent_api_node .drf_router
56+ router = self .drf_router if router is None else router
57+ api_endpoint = ApiEndpoint (pattern , parent_api_node , router )
3158 self .endpoints .append (api_endpoint )
3259
33- def _is_drf_view (self , pattern ):
34- """
35- Should check whether a pattern inherits from DRF's APIView
36- """
37- return hasattr (pattern .callback , 'cls' ) and issubclass (pattern .callback .cls , APIView )
60+ def get_endpoints (self ):
61+ return self .endpoints
3862
39- def _is_format_endpoint (self , pattern ):
40- """
41- Exclude endpoints with a "format" parameter
63+
64+ def _is_drf_view (pattern ):
65+ """
66+ Should check whether a pattern inherits from DRF's APIView
67+ """
68+ return hasattr (pattern .callback , 'cls' ) and issubclass (pattern .callback .cls ,
69+ APIView )
70+
71+
72+ def _is_format_endpoint (pattern ):
73+ """
74+ Exclude endpoints with a "format" parameter
75+ """
76+ return '?P<format>' in pattern ._regex
77+
78+
79+ def get_router (pattern ):
80+ urlconf = pattern .urlconf_name
81+ router = None
82+ if isinstance (urlconf , ModuleType ):
83+ # First: try MODULE_ROUTERS setting - Don't ignore errors
84+ router = get_module_router (urlconf )
85+ if router is not None :
86+ return router
87+ # Second: try DEFAULT_MODULE_ROUTER setting - Ignore errors
88+ try :
89+ router = get_default_module_router (urlconf )
90+ if router is not None :
91+ return router
92+ except :
93+ pass
94+ # Third: try DEFAULT_ROUTER setting - Don't ignore errors
95+ router = get_default_router ()
96+ if router is not None :
97+ return router
98+ return router
99+
100+
101+ def get_module_router (module ):
102+ routers = drf_settings ['MODULE_ROUTERS' ]
103+ if routers is None :
104+ return None
105+ if module .__name__ in routers :
106+ router_name = routers [module .__name__ ]
107+ router = getattr (module , router_name )
108+ assert isinstance (router , BaseRouter ), \
109+ """
110+ drfdocs 'ROUTERS' setting does not correspond to
111+ a router instance for module {}.
112+ """ .format (module .__name__ )
113+ return router
114+ return None
115+
116+
117+ def get_default_module_router (module ):
118+ default_module_router = drf_settings ['DEFAULT_MODULE_ROUTER' ]
119+ if default_module_router is None :
120+ return None
121+ router = getattr (module , default_module_router )
122+ assert isinstance (router , BaseRouter ), \
42123 """
43- return '?P<format>' in pattern ._regex
124+ drfdocs 'DEFAULT_MODULE_ROUTER' setting does not correspond to
125+ a router instance for module {}.
126+ """ .format (module .__name__ )
127+ return router
44128
45- def get_endpoints (self ):
46- return self .endpoints
129+
130+ def get_default_router ():
131+ default_router_path = drf_settings ['DEFAULT_ROUTER' ]
132+ if default_router_path is None :
133+ return None
134+ router = import_string (default_router_path )
135+ assert isinstance (router , BaseRouter ), \
136+ """
137+ drfdocs 'DEFAULT_ROUTER_NAME' setting does not correspond to
138+ a router instance {}.
139+ """ .format (router .__name__ )
140+ return router
0 commit comments