0
# Routers and URL Patterns
1
2
Automatic URL pattern generation for ViewSets with support for nested routes and custom actions in Django REST Framework.
3
4
## Capabilities
5
6
### Router Classes
7
8
Router classes automatically generate URL patterns from ViewSet registrations.
9
10
```python { .api }
11
class BaseRouter:
12
"""
13
Base class for all router implementations.
14
"""
15
def __init__(self):
16
self.registry = [] # List of registered viewsets
17
18
def register(self, prefix, viewset, basename=None):
19
"""
20
Register a viewset with the router.
21
22
Args:
23
prefix (str): URL prefix for the viewset
24
viewset: ViewSet class to register
25
basename (str): Base name for URL patterns
26
"""
27
28
def get_urls(self):
29
"""
30
Generate URL patterns for all registered viewsets.
31
32
Returns:
33
list: Django URL patterns
34
"""
35
36
def get_api_root_view(self, api_urls=None):
37
"""
38
Create API root view showing available endpoints.
39
40
Args:
41
api_urls: URL patterns to include in root
42
43
Returns:
44
APIView: Root view class
45
"""
46
47
def get_default_basename(self, viewset):
48
"""
49
Get default basename from viewset's queryset model.
50
51
Args:
52
viewset: ViewSet class
53
54
Returns:
55
str: Default basename
56
"""
57
58
class SimpleRouter(BaseRouter):
59
"""
60
Simple router generating basic URL patterns.
61
"""
62
routes = [ # URL pattern templates
63
# List route: /prefix/
64
Route(
65
url=r'^{prefix}{trailing_slash}$',
66
mapping={'get': 'list', 'post': 'create'},
67
name='{basename}-list',
68
detail=False,
69
initkwargs={'suffix': 'List'}
70
),
71
# Detail route: /prefix/{lookup}/
72
Route(
73
url=r'^{prefix}/{lookup}{trailing_slash}$',
74
mapping={
75
'get': 'retrieve',
76
'put': 'update',
77
'patch': 'partial_update',
78
'delete': 'destroy'
79
},
80
name='{basename}-detail',
81
detail=True,
82
initkwargs={'suffix': 'Instance'}
83
),
84
]
85
86
def __init__(self, trailing_slash=True):
87
"""
88
Args:
89
trailing_slash (bool): Include trailing slash in URLs
90
"""
91
self.trailing_slash = '/' if trailing_slash else ''
92
super().__init__()
93
94
class DefaultRouter(SimpleRouter):
95
"""
96
Default router with API root view and optional format suffixes.
97
"""
98
include_root_view = True # Include API root endpoint
99
include_format_suffixes = True # Include format suffixes (.json, .xml)
100
root_view_name = 'api-root' # Name for root view
101
default_schema_renderers = None # Schema renderers for root view
102
103
def get_api_root_view(self, api_urls=None):
104
"""
105
Create browsable API root view.
106
107
Returns:
108
APIRootView: Root view showing available endpoints
109
"""
110
```
111
112
### Route Configuration Classes
113
114
Classes defining URL route patterns and mappings.
115
116
```python { .api }
117
Route = namedtuple('Route', [
118
'url', # URL pattern template
119
'mapping', # HTTP method to viewset action mapping
120
'name', # URL pattern name template
121
'detail', # Whether route is for detail views
122
'initkwargs' # Additional viewset initialization kwargs
123
])
124
125
DynamicRoute = namedtuple('DynamicRoute', [
126
'url', # URL pattern template
127
'name', # URL pattern name template
128
'detail', # Whether route is for detail views
129
'initkwargs' # Additional viewset initialization kwargs
130
])
131
```
132
133
### API Root View
134
135
Automatically generated root view for browsable API.
136
137
```python { .api }
138
class APIRootView(APIView):
139
"""
140
Default API root view providing links to registered endpoints.
141
"""
142
_ignore_model_permissions = True
143
schema = None # Disable schema generation for root
144
api_root_dict = None # Dictionary of available endpoints
145
146
def get(self, request, *args, **kwargs):
147
"""
148
Return links to all available API endpoints.
149
150
Returns:
151
Response: Dictionary of endpoint names and URLs
152
"""
153
```
154
155
### URL Pattern Utilities
156
157
Utilities for working with URL patterns and format suffixes.
158
159
```python { .api }
160
def format_suffix_patterns(urlpatterns, suffix_required=False, allowed=None):
161
"""
162
Add format suffix patterns to existing URL patterns.
163
164
Args:
165
urlpatterns (list): Existing URL patterns
166
suffix_required (bool): Whether format suffix is required
167
allowed (list): List of allowed format suffixes
168
169
Returns:
170
list: URL patterns with format suffix support
171
"""
172
173
def apply_suffix_patterns(urlpatterns, suffix_pattern, suffix_required, allowed):
174
"""
175
Apply suffix patterns to URL patterns.
176
177
Args:
178
urlpatterns (list): URL patterns to modify
179
suffix_pattern (str): Suffix pattern template
180
suffix_required (bool): Whether suffix is required
181
allowed (list): Allowed suffix values
182
183
Returns:
184
list: Modified URL patterns
185
"""
186
```
187
188
## Usage Examples
189
190
### Basic Router Setup
191
192
```python
193
# urls.py
194
from rest_framework.routers import DefaultRouter
195
from myapp.views import BookViewSet, AuthorViewSet
196
197
# Create router instance
198
router = DefaultRouter()
199
200
# Register viewsets
201
router.register(r'books', BookViewSet)
202
router.register(r'authors', AuthorViewSet)
203
204
# Include router URLs
205
urlpatterns = [
206
path('admin/', admin.site.urls),
207
path('api/', include(router.urls)),
208
path('api-auth/', include('rest_framework.urls')),
209
]
210
211
# Generated URLs:
212
# /api/ (API root)
213
# /api/books/ (list/create books)
214
# /api/books/{id}/ (retrieve/update/delete book)
215
# /api/authors/ (list/create authors)
216
# /api/authors/{id}/ (retrieve/update/delete author)
217
```
218
219
### Custom Basename and ViewSet Actions
220
221
```python
222
from rest_framework.viewsets import ModelViewSet
223
from rest_framework.decorators import action
224
from rest_framework.response import Response
225
226
class BookViewSet(ModelViewSet):
227
queryset = Book.objects.all()
228
serializer_class = BookSerializer
229
230
@action(detail=True, methods=['post'])
231
def set_favorite(self, request, pk=None):
232
"""Mark book as favorite for current user."""
233
book = self.get_object()
234
# Custom logic here
235
return Response({'status': 'favorite set'})
236
237
@action(detail=False, methods=['get'])
238
def recent(self, request):
239
"""Get recently published books."""
240
recent_books = self.queryset.filter(
241
publication_date__gte=timezone.now() - timedelta(days=30)
242
)
243
serializer = self.get_serializer(recent_books, many=True)
244
return Response(serializer.data)
245
246
@action(detail=True, methods=['get'], url_path='reviews')
247
def book_reviews(self, request, pk=None):
248
"""Get reviews for specific book."""
249
book = self.get_object()
250
reviews = book.reviews.all()
251
# Serialize and return reviews
252
return Response(review_data)
253
254
# Register with custom basename
255
router.register(r'books', BookViewSet, basename='book')
256
257
# Generated URLs include custom actions:
258
# /api/books/{id}/set_favorite/ (POST)
259
# /api/books/recent/ (GET)
260
# /api/books/{id}/reviews/ (GET)
261
```
262
263
### Multiple Routers and Nested URLs
264
265
```python
266
# Main router for primary resources
267
main_router = DefaultRouter()
268
main_router.register(r'books', BookViewSet)
269
main_router.register(r'authors', AuthorViewSet)
270
271
# Secondary router for admin resources
272
admin_router = SimpleRouter()
273
admin_router.register(r'users', UserViewSet)
274
admin_router.register(r'logs', LogViewSet)
275
276
# Combine routers
277
urlpatterns = [
278
path('api/v1/', include(main_router.urls)),
279
path('api/admin/', include(admin_router.urls)),
280
# Manual URL patterns can be mixed in
281
path('api/auth/', include('rest_framework.authtoken.urls')),
282
]
283
```
284
285
### Custom Router with Special Routes
286
287
```python
288
from rest_framework.routers import Route, DynamicRoute, SimpleRouter
289
290
class CustomRouter(SimpleRouter):
291
"""
292
Custom router with additional route patterns.
293
"""
294
routes = [
295
# Standard list route
296
Route(
297
url=r'^{prefix}{trailing_slash}$',
298
mapping={'get': 'list', 'post': 'create'},
299
name='{basename}-list',
300
detail=False,
301
initkwargs={'suffix': 'List'}
302
),
303
# Custom bulk route
304
Route(
305
url=r'^{prefix}/bulk{trailing_slash}$',
306
mapping={'post': 'bulk_create', 'patch': 'bulk_update'},
307
name='{basename}-bulk',
308
detail=False,
309
initkwargs={}
310
),
311
# Standard detail route
312
Route(
313
url=r'^{prefix}/{lookup}{trailing_slash}$',
314
mapping={
315
'get': 'retrieve',
316
'put': 'update',
317
'patch': 'partial_update',
318
'delete': 'destroy'
319
},
320
name='{basename}-detail',
321
detail=True,
322
initkwargs={'suffix': 'Instance'}
323
),
324
# Dynamic routes for @action decorated methods
325
DynamicRoute(
326
url=r'^{prefix}/{lookup}/{url_path}{trailing_slash}$',
327
name='{basename}-{url_name}',
328
detail=True,
329
initkwargs={}
330
),
331
DynamicRoute(
332
url=r'^{prefix}/{url_path}{trailing_slash}$',
333
name='{basename}-{url_name}',
334
detail=False,
335
initkwargs={}
336
),
337
]
338
339
# Usage
340
custom_router = CustomRouter()
341
custom_router.register(r'books', BookViewSet)
342
343
# Now supports bulk operations:
344
# POST /api/books/bulk/ -> bulk_create
345
# PATCH /api/books/bulk/ -> bulk_update
346
```
347
348
### Format Suffix Patterns
349
350
```python
351
from rest_framework.urlpatterns import format_suffix_patterns
352
353
# Manual URL patterns with format suffixes
354
urlpatterns = [
355
path('books/', BookListView.as_view(), name='book-list'),
356
path('books/<int:pk>/', BookDetailView.as_view(), name='book-detail'),
357
]
358
359
# Add format suffix support
360
urlpatterns = format_suffix_patterns(urlpatterns)
361
362
# Now supports:
363
# /books/ or /books.json or /books.xml
364
# /books/1/ or /books/1.json or /books/1.xml
365
366
# Custom allowed formats
367
urlpatterns = format_suffix_patterns(
368
urlpatterns,
369
allowed=['json', 'xml', 'yaml']
370
)
371
372
# Required suffix
373
urlpatterns = format_suffix_patterns(
374
urlpatterns,
375
suffix_required=True # Must include format suffix
376
)
377
```
378
379
### Router without Root View
380
381
```python
382
# Simple router without API root
383
simple_router = SimpleRouter()
384
simple_router.register(r'books', BookViewSet)
385
386
# Default router without root view
387
no_root_router = DefaultRouter()
388
no_root_router.include_root_view = False
389
no_root_router.register(r'books', BookViewSet)
390
391
# Custom trailing slash behavior
392
no_slash_router = DefaultRouter(trailing_slash=False)
393
no_slash_router.register(r'books', BookViewSet)
394
395
# URLs without trailing slashes:
396
# /api/books (instead of /api/books/)
397
# /api/books/1 (instead of /api/books/1/)
398
```
399
400
### Debugging Router URLs
401
402
```python
403
# Print all generated URLs for debugging
404
router = DefaultRouter()
405
router.register(r'books', BookViewSet)
406
router.register(r'authors', AuthorViewSet)
407
408
# Get all URL patterns
409
url_patterns = router.urls
410
411
for pattern in url_patterns:
412
print(f"Pattern: {pattern.pattern}")
413
print(f"Name: {pattern.name}")
414
print(f"Callback: {pattern.callback}")
415
print("---")
416
```
417
418
## Utility Functions
419
420
```python { .api }
421
def escape_curly_brackets(url_path):
422
"""
423
Escape curly brackets in URL path for regex patterns.
424
425
Args:
426
url_path (str): URL path that may contain curly brackets
427
428
Returns:
429
str: Escaped URL path
430
"""
431
432
def flatten(list_of_lists):
433
"""
434
Flatten nested lists into single list.
435
436
Args:
437
list_of_lists (list): Nested list structure
438
439
Returns:
440
list: Flattened list
441
"""
442
443
def format_suffix_patterns(urlpatterns, suffix_required=False, allowed=None):
444
"""
445
Add format suffix patterns to URL patterns.
446
447
Args:
448
urlpatterns (list): List of URL patterns
449
suffix_required (bool): Whether format suffix is required
450
allowed (list): List of allowed format suffixes
451
452
Returns:
453
list: URL patterns with format suffix support
454
"""
455
456
def apply_suffix_patterns(urlpatterns, suffix_pattern, suffix_required, suffix_route=None):
457
"""
458
Apply suffix patterns to URL pattern list recursively.
459
Internal function used by format_suffix_patterns.
460
461
Args:
462
urlpatterns (list): URL patterns to process
463
suffix_pattern (str): Suffix pattern regex
464
suffix_required (bool): Whether suffix is required
465
suffix_route (str): Optional route suffix
466
467
Returns:
468
list: Processed URL patterns
469
"""
470
```