You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -36,6 +41,12 @@ retrieve the page can be customized by subclassing `PageNumberPagination` and
36
41
overriding the `page_query_param`. Page size can be controlled per request via
37
42
the `PAGINATE_BY_PARAM` query parameter (`page_size` by default).
38
43
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
+
39
50
### Serializers
40
51
41
52
It is recommended to import the base serializer classes from this package
@@ -558,6 +569,39 @@ class QuestSerializer(serializers.ModelSerializer):
558
569
`included_resources` informs DJA of **what** you would like to include.
559
570
`included_serializers` tells DJA **how** you want to include it.
560
571
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>
0 commit comments