0
# Request and Response
1
2
Enhanced request and response objects providing consistent data access and content negotiation across different formats in Django REST Framework.
3
4
## Capabilities
5
6
### Request Object
7
8
Enhanced request object wrapping Django's HttpRequest with additional API functionality.
9
10
```python { .api }
11
class Request:
12
"""
13
Enhanced request object with additional API features.
14
"""
15
def __init__(self, request, parsers=None, authenticators=None,
16
negotiator=None, parser_context=None):
17
"""
18
Initialize DRF Request object.
19
20
Args:
21
request: Django HttpRequest object
22
parsers (list): List of parser instances
23
authenticators (list): List of authenticator instances
24
negotiator: Content negotiation instance
25
parser_context (dict): Additional context for parsers
26
"""
27
28
@property
29
def data(self):
30
"""
31
Parsed request body data.
32
33
Returns:
34
dict or list: Parsed data from request body
35
"""
36
37
@property
38
def query_params(self):
39
"""
40
Query parameters from URL (more semantic than request.GET).
41
42
Returns:
43
QueryDict: URL query parameters
44
"""
45
46
@property
47
def user(self):
48
"""
49
Authenticated user for this request.
50
51
Returns:
52
User instance or AnonymousUser
53
"""
54
55
@property
56
def auth(self):
57
"""
58
Authentication credentials for this request.
59
60
Returns:
61
Authentication token/credentials or None
62
"""
63
64
@property
65
def successful_authenticator(self):
66
"""
67
Authenticator instance that successfully authenticated the request.
68
69
Returns:
70
Authentication instance or None
71
"""
72
73
@property
74
def accepted_renderer(self):
75
"""
76
Renderer selected by content negotiation.
77
78
Returns:
79
Renderer instance
80
"""
81
82
@property
83
def accepted_media_type(self):
84
"""
85
Media type selected by content negotiation.
86
87
Returns:
88
str: Media type string
89
"""
90
91
@property
92
def version(self):
93
"""
94
API version for this request.
95
96
Returns:
97
str: Version string or None
98
"""
99
100
@property
101
def versioning_scheme(self):
102
"""
103
Versioning scheme instance for this request.
104
105
Returns:
106
Versioning scheme instance or None
107
"""
108
```
109
110
### Response Object
111
112
Enhanced response object for API responses with content negotiation support.
113
114
```python { .api }
115
class Response(SimpleTemplateResponse):
116
"""
117
Enhanced response object for API responses.
118
"""
119
def __init__(self, data=None, status=None, template_name=None,
120
headers=None, exception=False, content_type=None):
121
"""
122
Initialize API response.
123
124
Args:
125
data: Response data (will be serialized)
126
status (int): HTTP status code
127
template_name (str): Template for HTML rendering
128
headers (dict): Additional response headers
129
exception (bool): Whether response is for exception
130
content_type (str): Response content type
131
"""
132
133
@property
134
def data(self):
135
"""
136
Response data to be serialized.
137
138
Returns:
139
Response data
140
"""
141
142
@data.setter
143
def data(self, value):
144
"""
145
Set response data.
146
147
Args:
148
value: Data to serialize in response
149
"""
150
151
@property
152
def status_code(self):
153
"""
154
HTTP status code for response.
155
156
Returns:
157
int: HTTP status code
158
"""
159
160
@status_code.setter
161
def status_code(self, value):
162
"""
163
Set HTTP status code.
164
165
Args:
166
value (int): HTTP status code
167
"""
168
169
@property
170
def status_text(self):
171
"""
172
HTTP status text for response.
173
174
Returns:
175
str: HTTP status text
176
"""
177
178
@property
179
def rendered_content(self):
180
"""
181
Rendered response content.
182
183
Returns:
184
bytes: Rendered content
185
"""
186
```
187
188
### Request Utilities
189
190
Utility classes and functions for request handling.
191
192
```python { .api }
193
class ForcedAuthentication:
194
"""
195
Force specific user authentication for testing.
196
"""
197
def __init__(self, user, token=None):
198
"""
199
Args:
200
user: User instance to authenticate as
201
token: Optional authentication token
202
"""
203
self.user = user
204
self.token = token
205
206
class Empty:
207
"""
208
Placeholder for empty request data.
209
"""
210
pass
211
212
def is_form_media_type(media_type):
213
"""
214
Check if media type represents form data.
215
216
Args:
217
media_type (str): Media type to check
218
219
Returns:
220
bool: True if form media type
221
"""
222
223
def clone_request(request, method):
224
"""
225
Clone request with different HTTP method.
226
227
Args:
228
request: Original request object
229
method (str): New HTTP method
230
231
Returns:
232
Request: Cloned request with new method
233
"""
234
235
class override_method:
236
"""
237
Context manager for temporarily overriding request method.
238
"""
239
def __init__(self, request, method):
240
"""
241
Args:
242
request: Request object to modify
243
method (str): Temporary method to use
244
"""
245
self.request = request
246
self.method = method
247
self.original_method = None
248
249
def __enter__(self):
250
"""Enter context with overridden method."""
251
return self
252
253
def __exit__(self, exc_type, exc_val, exc_tb):
254
"""Restore original method."""
255
```
256
257
## Usage Examples
258
259
### Accessing Request Data
260
261
```python
262
from rest_framework.views import APIView
263
from rest_framework.response import Response
264
265
class BookView(APIView):
266
def post(self, request):
267
# Access parsed request data (JSON, form data, etc.)
268
title = request.data.get('title')
269
author = request.data.get('author')
270
271
# Access query parameters
272
format_type = request.query_params.get('format', 'json')
273
274
# Access authenticated user
275
user = request.user
276
277
# Access authentication token/credentials
278
auth_token = request.auth
279
280
# Create response
281
response_data = {
282
'message': f'Book "{title}" by {author} created',
283
'user': user.username if user.is_authenticated else 'anonymous',
284
'format': format_type
285
}
286
287
return Response(response_data, status=201)
288
```
289
290
### Different Response Types
291
292
```python
293
from rest_framework.response import Response
294
from rest_framework import status
295
296
class BookAPIView(APIView):
297
def get(self, request):
298
# Simple data response
299
data = {'books': ['Book 1', 'Book 2']}
300
return Response(data)
301
302
def post(self, request):
303
# Response with custom status
304
return Response(
305
{'message': 'Book created'},
306
status=status.HTTP_201_CREATED
307
)
308
309
def put(self, request):
310
# Response with custom headers
311
headers = {'X-Custom-Header': 'custom-value'}
312
return Response(
313
{'message': 'Book updated'},
314
headers=headers
315
)
316
317
def delete(self, request):
318
# Empty response with status code
319
return Response(status=status.HTTP_204_NO_CONTENT)
320
```
321
322
### Content Negotiation
323
324
```python
325
class BookView(APIView):
326
def get(self, request):
327
books = Book.objects.all()
328
329
# Check accepted media type
330
if request.accepted_media_type == 'application/json':
331
# JSON response
332
serializer = BookSerializer(books, many=True)
333
return Response(serializer.data)
334
elif request.accepted_media_type == 'text/html':
335
# HTML response
336
return Response(
337
{'books': books},
338
template_name='books/list.html'
339
)
340
else:
341
# Default response
342
return Response({'error': 'Unsupported media type'})
343
```
344
345
### API Versioning
346
347
API versioning allows maintaining backward compatibility while evolving APIs. Multiple versioning strategies are supported.
348
349
#### Versioning Classes
350
351
```python { .api }
352
class BaseVersioning:
353
"""
354
Base class for all versioning schemes.
355
"""
356
default_version = None
357
allowed_versions = None
358
version_param = 'version'
359
360
def determine_version(self, request, *args, **kwargs):
361
"""
362
Determine API version from request.
363
Must be implemented by subclasses.
364
365
Returns:
366
str: Version string
367
"""
368
369
def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):
370
"""
371
Reverse URL with version information.
372
"""
373
374
class AcceptHeaderVersioning(BaseVersioning):
375
"""
376
Version determined from Accept header media type parameters.
377
Example: Accept: application/json; version=1.0
378
"""
379
380
class URLPathVersioning(BaseVersioning):
381
"""
382
Version determined from URL path keyword arguments.
383
Example: /v1/books/
384
"""
385
386
class NamespaceVersioning(BaseVersioning):
387
"""
388
Version determined from URL namespace.
389
Example: URL patterns with version namespaces
390
"""
391
392
class HostNameVersioning(BaseVersioning):
393
"""
394
Version determined from request hostname.
395
Example: v1.api.example.com
396
"""
397
398
class QueryParameterVersioning(BaseVersioning):
399
"""
400
Version determined from query parameters.
401
Example: /books/?version=1.0
402
"""
403
```
404
405
#### Versioning Configuration
406
407
Configure versioning in Django settings:
408
409
```python
410
# settings.py
411
REST_FRAMEWORK = {
412
'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.AcceptHeaderVersioning',
413
'DEFAULT_VERSION': '1.0',
414
'ALLOWED_VERSIONS': ['1.0', '1.1', '2.0'],
415
'VERSION_PARAM': 'version',
416
}
417
```
418
419
#### URL Path Versioning Example
420
421
```python
422
# urls.py
423
from django.urls import path, include
424
425
urlpatterns = [
426
path('v1/', include('myapp.urls', namespace='v1')),
427
path('v2/', include('myapp.urls', namespace='v2')),
428
]
429
430
# Using in views
431
class BookViewSet(ModelViewSet):
432
def list(self, request):
433
version = request.version
434
435
if version == '1.0':
436
# Version 1.0 response format
437
serializer = BookSerializerV1(books, many=True)
438
elif version == '2.0':
439
# Version 2.0 response format
440
serializer = BookSerializerV2(books, many=True)
441
442
return Response(serializer.data)
443
```
444
445
#### Accept Header Versioning Example
446
447
```python
448
# Client request:
449
# Accept: application/json; version=1.1
450
451
class BookAPIView(APIView):
452
def get(self, request):
453
version = request.version # '1.1'
454
455
books = Book.objects.all()
456
if version == '1.0':
457
data = [{'id': b.id, 'title': b.title} for b in books]
458
else: # version >= 1.1
459
data = [{'id': b.id, 'title': b.title, 'author': b.author} for b in books]
460
461
return Response({'books': data, 'version': version})
462
```
463
464
#### Custom Versioning Scheme
465
466
```python
467
from rest_framework.versioning import BaseVersioning
468
from rest_framework import exceptions
469
470
class CustomHeaderVersioning(BaseVersioning):
471
"""
472
Custom versioning based on X-API-Version header.
473
"""
474
def determine_version(self, request, *args, **kwargs):
475
version = request.META.get('HTTP_X_API_VERSION', self.default_version)
476
477
if not self.is_allowed_version(version):
478
raise exceptions.NotAcceptable(
479
'Invalid API version. Supported versions: {}'.format(
480
', '.join(self.allowed_versions)
481
)
482
)
483
484
return version
485
```
486
487
### Working with File Uploads
488
489
```python
490
class FileUploadView(APIView):
491
def post(self, request):
492
# Access uploaded files
493
uploaded_file = request.data.get('file')
494
495
if uploaded_file:
496
# Process the file
497
file_content = uploaded_file.read()
498
file_name = uploaded_file.name
499
file_size = uploaded_file.size
500
501
return Response({
502
'message': 'File uploaded successfully',
503
'filename': file_name,
504
'size': file_size
505
})
506
507
return Response(
508
{'error': 'No file provided'},
509
status=status.HTTP_400_BAD_REQUEST
510
)
511
```
512
513
### Request Method Override
514
515
```python
516
from rest_framework.request import override_method
517
518
class CustomView(APIView):
519
def post(self, request):
520
# Temporarily override method for internal processing
521
with override_method(request, 'PUT'):
522
# Process as if it were a PUT request
523
return self.handle_update(request)
524
525
def handle_update(self, request):
526
# Method sees request.method as 'PUT'
527
return Response({'method': request.method})
528
```
529
530
### Testing with Forced Authentication
531
532
```python
533
from rest_framework.request import ForcedAuthentication
534
from rest_framework.test import APIRequestFactory
535
536
# In tests
537
factory = APIRequestFactory()
538
user = User.objects.create_user('testuser')
539
540
# Create request with forced authentication
541
request = factory.post('/api/books/', {'title': 'Test Book'})
542
force_auth = ForcedAuthentication(user)
543
544
# Apply authentication
545
request.user = user
546
request.auth = None
547
```