or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

auth-permissions.mdcontent-negotiation.mddecorators.mdfields-validation.mdgeneric-views.mdindex.mdpagination-filtering.mdrequest-response.mdrouters-urls.mdserializers.mdstatus-exceptions.mdtesting.mdviews-viewsets.md

routers-urls.mddocs/

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

```