Skip to content

Commit f0bda69

Browse files
committed
Updating docs to reflect the added ModelViewSet helper
1 parent f924ac8 commit f0bda69

File tree

1 file changed

+45
-1
lines changed

1 file changed

+45
-1
lines changed

docs/usage.md

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,12 @@ REST_FRAMEWORK = {
2323
),
2424
'DEFAULT_RENDERER_CLASSES': (
2525
'rest_framework_json_api.renderers.JSONRenderer',
26-
'rest_framework.renderers.BrowsableAPIRenderer',
26+
# If you're performance testing, you will want to use the browseable API
27+
# without forms, as the forms can generate their own queries.
28+
# If performance testing, enable:
29+
# 'example.utils.BrowsableAPIRendererWithoutForms',
30+
# Otherwise, to play around with the browseable API, enable:
31+
'rest_framework.renderers.BrowsableAPIRenderer'
2732
),
2833
'DEFAULT_METADATA_CLASS': 'rest_framework_json_api.metadata.JSONAPIMetadata',
2934
}
@@ -36,6 +41,12 @@ retrieve the page can be customized by subclassing `PageNumberPagination` and
3641
overriding the `page_query_param`. Page size can be controlled per request via
3742
the `PAGINATE_BY_PARAM` query parameter (`page_size` by default).
3843

44+
#### Performance Testing
45+
46+
If you are trying to see if your viewsets are configured properly to optimize performance,
47+
it is preferable to use `example.utils.BrowsableAPIRendererWithoutForms` instead of the default `BrowsableAPIRenderer`
48+
to remove queries introduced by the forms themselves.
49+
3950
### Serializers
4051

4152
It is recommended to import the base serializer classes from this package
@@ -558,6 +569,39 @@ class QuestSerializer(serializers.ModelSerializer):
558569
`included_resources` informs DJA of **what** you would like to include.
559570
`included_serializers` tells DJA **how** you want to include it.
560571

572+
#### Performance improvements
573+
574+
Be aware that using included resources without any form of prefetching **WILL HURT PERFORMANCE** as it will introduce m*(n+1) queries.
575+
576+
A viewset helper was designed to allow for greater flexibility and it is automatically available when subclassing
577+
`views.ModelViewSet`
578+
```
579+
# When MyViewSet is called with ?include=author it will dynamically prefetch author and author.bio
580+
class MyViewSet(viewsets.ModelViewSet):
581+
queryset = Book.objects.all()
582+
prefetch_for_includes = {
583+
'__all__': [],
584+
'author': ['author', 'author__bio']
585+
'category.section': ['category']
586+
}
587+
```
588+
589+
The special keyword `__all__` can be used to specify a prefetch which should be done regardless of the include, similar to making the prefetch yourself on the QuerySet.
590+
591+
Using the helper instead of prefetching/selecting everything manually will prevent django from trying to load what could be a significant amount of data in memory for every single request.
592+
593+
> If you have a single model, e.g. Book, which has four relations e.g. Author, Publisher, CopyrightHolder, Category.
594+
>
595+
> To display 25 books in DRF without any includes, I would need a single query: SELECT * FROM book.
596+
>
597+
> To display 25 books DRF-JSONAPI without any includes, I would need either:
598+
> a) 1 query ala SELECT * FROM books LEFT JOIN author LEFT JOIN publisher LEFT JOIN CopyrightHolder LEFT JOIN Category
599+
> b) 4 queries with prefetches.
600+
>
601+
> Let's say I have 1M books, 50k authors, 10k categories, 10k copyrightholders. In the select_related scenario, you've just created a in-memory table with 1e18 rows ... do this a few times per second and you have melted your database. All to display 25 rows, with no included relationships. So select_related is only going to work if you have a small dataset or a small volume of traffic.
602+
>
603+
> -- <cite> Aidan Lister in issue [#337](https://github.com/django-json-api/django-rest-framework-json-api/issues/337#issuecomment-297335342)</cite>
604+
561605
<!--
562606
### Relationships
563607
### Errors

0 commit comments

Comments
 (0)