Skip to content

Commit 6ea95b6

Browse files
thetarbyxordoquy
andauthored
Highlight select_related and prefetch_related usage in documentation (encode#7610)
* docs updated to highlight use of select_related and prefetch related to avoid n+1 problems * Apply suggestions from code review cosmetic changes Co-authored-by: Xavier Ordoquy <xordoquy@linovia.com> * cosmetic changes Co-authored-by: Xavier Ordoquy <xordoquy@linovia.com>
1 parent 605a624 commit 6ea95b6

File tree

3 files changed

+47
-1
lines changed

3 files changed

+47
-1
lines changed

docs/api-guide/fields.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,14 @@ Defaults to `False`
7878

7979
### `source`
8080

81-
The name of the attribute that will be used to populate the field. May be a method that only takes a `self` argument, such as `URLField(source='get_absolute_url')`, or may use dotted notation to traverse attributes, such as `EmailField(source='user.email')`. When serializing fields with dotted notation, it may be necessary to provide a `default` value if any object is not present or is empty during attribute traversal.
81+
The name of the attribute that will be used to populate the field. May be a method that only takes a `self` argument, such as `URLField(source='get_absolute_url')`, or may use dotted notation to traverse attributes, such as `EmailField(source='user.email')`.
82+
83+
When serializing fields with dotted notation, it may be necessary to provide a `default` value if any object is not present or is empty during attribute traversal. Beware of possible n+1 problems when using source attribute if you are accessing a relational orm model. For example:
84+
85+
class CommentSerializer(serializers.Serializer):
86+
email = serializers.EmailField(source="user.email")
87+
88+
would require user object to be fetched from database when it is not prefetched. If that is not wanted, be sure to be using `prefetch_related` and `select_related` methods appropriately. For more information about the methods refer to [django documentation][django-docs-select-related].
8289

8390
The value `source='*'` has a special meaning, and is used to indicate that the entire object should be passed through to the field. This can be useful for creating nested representations, or for fields which require access to the complete object in order to determine the output representation.
8491

@@ -855,3 +862,4 @@ The [django-rest-framework-hstore][django-rest-framework-hstore] package provide
855862
[django-hstore]: https://github.com/djangonauts/django-hstore
856863
[python-decimal-rounding-modes]: https://docs.python.org/3/library/decimal.html#rounding-modes
857864
[django-current-timezone]: https://docs.djangoproject.com/en/stable/topics/i18n/timezones/#default-time-zone-and-current-time-zone
865+
[django-docs-select-related]: https://docs.djangoproject.com/en/3.1/ref/models/querysets/#django.db.models.query.QuerySet.select_related

docs/api-guide/generic-views.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,12 @@ For example:
9696
user = self.request.user
9797
return user.accounts.all()
9898

99+
---
100+
101+
**Note:** If the serializer_class used in the generic view spans orm relations, leading to an n+1 problem, you could optimize your queryset in this method using `select_related` and `prefetch_related`. To get more information about n+1 problem and use cases of the mentioned methods refer to related section in [django documentation][django-docs-select-related].
102+
103+
---
104+
99105
#### `get_object(self)`
100106

101107
Returns an object instance that should be used for detail views. Defaults to using the `lookup_field` parameter to filter the base queryset.
@@ -389,3 +395,4 @@ The following third party packages provide additional generic view implementatio
389395
[UpdateModelMixin]: #updatemodelmixin
390396
[DestroyModelMixin]: #destroymodelmixin
391397
[django-rest-multiple-models]: https://github.com/MattBroach/DjangoRestMultipleModels
398+
[django-docs-select-related]: https://docs.djangoproject.com/en/3.1/ref/models/querysets/#django.db.models.query.QuerySet.select_related

docs/api-guide/relations.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,37 @@ Relational fields are used to represent model relationships. They can be applie
1717

1818
---
1919

20+
---
21+
22+
**Note:** REST Framework does not attempt to automatically optimize querysets passed to serializers in terms of `select_related` and `prefetch_related` since it would be too much magic. A serializer with a field spanning an orm relation through its source attribute could require an additional database hit to fetch related object from the database. It is the programmer's responsibility to optimize queries to avoid additional database hits which could occur while using such a serializer.
23+
24+
For example, the following serializer would lead to a database hit each time evaluating the tracks field if it is not prefetched:
25+
26+
class AlbumSerializer(serializers.ModelSerializer):
27+
tracks = serializers.SlugRelatedField(
28+
many=True,
29+
read_only=True,
30+
slug_field='title'
31+
)
32+
33+
class Meta:
34+
model = Album
35+
fields = ['album_name', 'artist', 'tracks']
36+
37+
# For each album object, tracks should be fetched from database
38+
qs = Album.objects.all()
39+
print(AlbumSerializer(qs, many=True).data)
40+
41+
If `AlbumSerializer` is used to serialize a fairly large queryset with `many=True` then it could be a serious performance problem. Optimizing the queryset passed to `AlbumSerializer` with:
42+
43+
qs = Album.objects.prefetch_related('tracks')
44+
# No additional database hits required
45+
print(AlbumSerializer(qs, many=True).data)
46+
47+
would solve the issue.
48+
49+
---
50+
2051
#### Inspecting relationships.
2152

2253
When using the `ModelSerializer` class, serializer fields and relationships will be automatically generated for you. Inspecting these automatically generated fields can be a useful tool for determining how to customize the relationship style.

0 commit comments

Comments
 (0)