0
# Exception Handling and Utilities
1
2
JSON:API compliant error formatting, field name formatting utilities, resource type management, and settings configuration for customizing JSON:API behavior.
3
4
## Capabilities
5
6
### Exception Handling
7
8
#### exception_handler
9
10
Main exception handler that converts Django REST framework errors to JSON:API format.
11
12
```python { .api }
13
def exception_handler(exc, context):
14
"""
15
JSON:API compliant exception handler.
16
17
Converts Django REST framework exceptions to JSON:API error format
18
with proper error objects containing status, detail, and source information.
19
20
Args:
21
exc: Exception instance
22
context: Exception context with view and request information
23
24
Returns:
25
Response: JSON:API formatted error response or None
26
"""
27
```
28
29
Usage example:
30
31
```python
32
# settings.py
33
REST_FRAMEWORK = {
34
'EXCEPTION_HANDLER': 'rest_framework_json_api.exceptions.exception_handler',
35
}
36
37
# Validation errors become:
38
# {
39
# "errors": [{
40
# "status": "400",
41
# "detail": "This field is required.",
42
# "source": {"pointer": "/data/attributes/title"}
43
# }]
44
# }
45
46
# Permission errors become:
47
# {
48
# "errors": [{
49
# "status": "403",
50
# "detail": "You do not have permission to perform this action."
51
# }]
52
# }
53
```
54
55
#### rendered_with_json_api
56
57
Utility function to check if a view uses JSON:API renderer.
58
59
```python { .api }
60
def rendered_with_json_api(view):
61
"""
62
Check if view is rendered with JSON:API renderer.
63
64
Args:
65
view: View instance
66
67
Returns:
68
bool: True if view uses JSONRenderer
69
"""
70
```
71
72
#### Conflict
73
74
HTTP 409 Conflict exception for JSON:API responses.
75
76
```python { .api }
77
class Conflict(exceptions.APIException):
78
"""
79
HTTP 409 Conflict exception.
80
81
Used for resource conflicts in JSON:API applications.
82
"""
83
84
status_code = 409
85
default_detail = "Conflict."
86
```
87
88
Usage example:
89
90
```python
91
from rest_framework_json_api.exceptions import Conflict
92
93
def update_article(self, instance, validated_data):
94
if instance.locked:
95
raise Conflict("Article is locked and cannot be modified.")
96
return super().update(instance, validated_data)
97
```
98
99
### Utilities
100
101
#### Resource Name and Type Management
102
103
```python { .api }
104
def get_resource_name(context, expand_polymorphic_types=False):
105
"""
106
Get resource name from view context.
107
108
Args:
109
context: Serializer context with view and request
110
expand_polymorphic_types: Whether to expand polymorphic types
111
112
Returns:
113
str or list: Resource name or list of polymorphic types
114
"""
115
116
def get_resource_type_from_model(model):
117
"""
118
Get resource type from Django model.
119
120
Args:
121
model: Django model class
122
123
Returns:
124
str: Resource type name
125
"""
126
127
def get_resource_type_from_serializer(serializer):
128
"""
129
Get resource type from serializer.
130
131
Args:
132
serializer: Serializer instance or class
133
134
Returns:
135
str: Resource type name
136
"""
137
138
def get_resource_type_from_instance(instance):
139
"""
140
Get resource type from model instance.
141
142
Args:
143
instance: Django model instance
144
145
Returns:
146
str: Resource type name
147
"""
148
149
def get_resource_type_from_queryset(queryset):
150
"""
151
Get resource type from QuerySet.
152
153
Args:
154
queryset: Django QuerySet
155
156
Returns:
157
str: Resource type name
158
"""
159
```
160
161
#### Field Name Formatting
162
163
```python { .api }
164
def format_field_names(obj, format_type=None):
165
"""
166
Format all field names in an object.
167
168
Args:
169
obj: Dict or object with field names to format
170
format_type: Format type override
171
172
Returns:
173
dict: Object with formatted field names
174
"""
175
176
def format_field_name(value, format_type=None):
177
"""
178
Format a single field name.
179
180
Args:
181
value: Field name to format
182
format_type: Format type override
183
184
Returns:
185
str: Formatted field name
186
"""
187
188
def undo_format_field_names(obj):
189
"""
190
Reverse field name formatting.
191
192
Args:
193
obj: Dict with formatted field names
194
195
Returns:
196
dict: Object with original field names
197
"""
198
199
def undo_format_field_name(value):
200
"""
201
Reverse format a single field name.
202
203
Args:
204
value: Formatted field name
205
206
Returns:
207
str: Original field name
208
"""
209
```
210
211
Usage example:
212
213
```python
214
# With JSON_API_FORMAT_FIELD_NAMES = True
215
format_field_name('created_at') # Returns 'created-at'
216
format_field_name('author_name') # Returns 'author-name'
217
218
undo_format_field_name('created-at') # Returns 'created_at'
219
undo_format_field_name('author-name') # Returns 'author_name'
220
```
221
222
#### Resource and Serializer Utilities
223
224
```python { .api }
225
def get_serializer_fields(serializer):
226
"""
227
Get fields from serializer, handling both regular and list serializers.
228
229
Args:
230
serializer: Serializer instance
231
232
Returns:
233
dict or None: Serializer fields
234
"""
235
236
def get_included_resources(request, serializer_class):
237
"""
238
Parse include parameter from request.
239
240
Args:
241
request: HTTP request object
242
serializer_class: Serializer class
243
244
Returns:
245
list: List of include paths
246
"""
247
248
def get_resource_id(resource_instance):
249
"""
250
Get resource ID from instance.
251
252
Args:
253
resource_instance: Model instance
254
255
Returns:
256
str: Resource ID
257
"""
258
```
259
260
#### Error Formatting
261
262
```python { .api }
263
def format_errors(data):
264
"""
265
Format errors in JSON:API format.
266
267
Args:
268
data: Error data to format
269
270
Returns:
271
dict: JSON:API formatted errors
272
"""
273
274
def format_drf_errors(response, context, exc):
275
"""
276
Format Django REST framework errors as JSON:API errors.
277
278
Args:
279
response: DRF error response
280
context: Exception context
281
exc: Exception instance
282
283
Returns:
284
Response: JSON:API formatted error response
285
"""
286
```
287
288
#### Hyperlink Support
289
290
```python { .api }
291
class Hyperlink:
292
"""
293
Hyperlink representation for JSON:API links.
294
295
Represents hyperlinks in JSON:API responses with href and meta.
296
"""
297
298
def __init__(self, href, meta=None):
299
"""
300
Initialize hyperlink.
301
302
Args:
303
href: Link URL
304
meta: Optional link metadata
305
"""
306
self.href = href
307
self.meta = meta
308
```
309
310
### Metadata
311
312
#### JSONAPIMetadata
313
314
JSON:API metadata implementation for OPTIONS requests.
315
316
```python { .api }
317
class JSONAPIMetadata(SimpleMetadata):
318
"""
319
JSON:API metadata implementation for OPTIONS responses.
320
321
Provides field information and relationship metadata
322
in a JSON:API compatible format.
323
"""
324
325
type_lookup = {
326
# Field type mappings for metadata
327
}
328
329
relation_type_lookup = {
330
# Relationship type mappings
331
}
332
333
def determine_metadata(self, request, view):
334
"""
335
Generate metadata for view.
336
337
Args:
338
request: HTTP request
339
view: View instance
340
341
Returns:
342
dict: Metadata information
343
"""
344
```
345
346
### Settings
347
348
#### JSONAPISettings
349
350
Settings management class for JSON:API configuration.
351
352
```python { .api }
353
class JSONAPISettings:
354
"""
355
Settings object for JSON:API configuration.
356
357
Allows JSON:API settings to be accessed as properties
358
with fallback to defaults.
359
"""
360
361
def __init__(self, user_settings=None, defaults=None):
362
"""
363
Initialize settings.
364
365
Args:
366
user_settings: User-defined settings
367
defaults: Default settings
368
"""
369
370
def __getattr__(self, attr):
371
"""
372
Get setting value with fallback to default.
373
374
Args:
375
attr: Setting name
376
377
Returns:
378
Setting value
379
380
Raises:
381
AttributeError: If setting is invalid
382
"""
383
```
384
385
#### Settings Instance
386
387
```python { .api }
388
json_api_settings = JSONAPISettings()
389
"""Global JSON:API settings instance."""
390
391
def reload_json_api_settings(*args, **kwargs):
392
"""
393
Reload JSON:API settings when Django settings change.
394
395
Connected to Django's setting_changed signal.
396
"""
397
```
398
399
### Configuration Options
400
401
Available JSON:API settings:
402
403
```python
404
# settings.py
405
JSON_API_FORMAT_FIELD_NAMES = True # Convert snake_case to kebab-case
406
JSON_API_FORMAT_TYPES = True # Format resource type names
407
JSON_API_FORMAT_RELATED_LINKS = True # Format related link segments
408
JSON_API_PLURALIZE_TYPES = True # Pluralize resource type names
409
JSON_API_UNIFORM_EXCEPTIONS = True # Use JSON:API errors everywhere
410
```
411
412
#### FORMAT_FIELD_NAMES
413
414
```python
415
# When True:
416
# Model field: created_at -> JSON:API: "created-at"
417
# Model field: author_name -> JSON:API: "author-name"
418
419
# When False:
420
# Model field: created_at -> JSON:API: "created_at"
421
# Model field: author_name -> JSON:API: "author_name"
422
```
423
424
#### FORMAT_TYPES
425
426
```python
427
# When True:
428
# Model: BlogPost -> Resource type: "blog-posts"
429
# Model: UserProfile -> Resource type: "user-profiles"
430
431
# When False:
432
# Model: BlogPost -> Resource type: "BlogPost"
433
# Model: UserProfile -> Resource type: "UserProfile"
434
```
435
436
#### PLURALIZE_TYPES
437
438
```python
439
# When True:
440
# Model: Article -> Resource type: "articles"
441
# Model: Category -> Resource type: "categories"
442
443
# When False:
444
# Model: Article -> Resource type: "article"
445
# Model: Category -> Resource type: "category"
446
```
447
448
#### UNIFORM_EXCEPTIONS
449
450
```python
451
# When True: All views use JSON:API error format
452
# When False: Only JSON:API views use JSON:API error format
453
```
454
455
## Error Response Format
456
457
JSON:API error responses follow this structure:
458
459
```python
460
{
461
"errors": [
462
{
463
"id": "unique-error-id", # Optional
464
"status": "400", # HTTP status code
465
"code": "validation_error", # Application-specific error code
466
"title": "Validation Error", # Human-readable title
467
"detail": "This field is required.", # Detailed error message
468
"source": { # Error source information
469
"pointer": "/data/attributes/title", # JSON Pointer to error location
470
"parameter": "filter[invalid]" # Query parameter that caused error
471
},
472
"meta": { # Additional metadata
473
"field": "title"
474
}
475
}
476
]
477
}
478
```
479
480
## Common Error Examples
481
482
### Validation Errors
483
484
```python
485
# Field validation error
486
{
487
"errors": [{
488
"status": "400",
489
"detail": "This field is required.",
490
"source": {"pointer": "/data/attributes/title"}
491
}]
492
}
493
494
# Relationship validation error
495
{
496
"errors": [{
497
"status": "400",
498
"detail": "Invalid pk \"999\" - object does not exist.",
499
"source": {"pointer": "/data/relationships/author"}
500
}]
501
}
502
```
503
504
### Authentication/Permission Errors
505
506
```python
507
# Authentication required
508
{
509
"errors": [{
510
"status": "401",
511
"detail": "Authentication credentials were not provided."
512
}]
513
}
514
515
# Permission denied
516
{
517
"errors": [{
518
"status": "403",
519
"detail": "You do not have permission to perform this action."
520
}]
521
}
522
```
523
524
### Not Found Errors
525
526
```python
527
{
528
"errors": [{
529
"status": "404",
530
"detail": "Not found."
531
}]
532
}
533
```
534
535
## Types
536
537
```python { .api }
538
from rest_framework_json_api.exceptions import (
539
exception_handler,
540
rendered_with_json_api,
541
Conflict
542
)
543
544
from rest_framework_json_api.utils import (
545
get_resource_name,
546
get_resource_type_from_model,
547
get_resource_type_from_serializer,
548
get_resource_type_from_instance,
549
format_field_names,
550
format_field_name,
551
undo_format_field_names,
552
undo_format_field_name,
553
get_serializer_fields,
554
get_included_resources,
555
format_errors,
556
format_drf_errors,
557
Hyperlink
558
)
559
560
from rest_framework_json_api.metadata import JSONAPIMetadata
561
from rest_framework_json_api.settings import (
562
JSONAPISettings,
563
json_api_settings,
564
reload_json_api_settings
565
)
566
```