From 84999ff17b7c4fb6615eb272914d8013ca2e8a1e Mon Sep 17 00:00:00 2001 From: Anton-Shutik Date: Mon, 18 Mar 2019 15:57:49 +0300 Subject: [PATCH 01/11] Added SelectAndPrefetchForIncludesMixin, deprecated PrefetchForIncludesHelperMixin --- rest_framework_json_api/views.py | 54 ++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/rest_framework_json_api/views.py b/rest_framework_json_api/views.py index 4b4c6ce3..0b6a3570 100644 --- a/rest_framework_json_api/views.py +++ b/rest_framework_json_api/views.py @@ -1,3 +1,4 @@ +import warnings from django.core.exceptions import ImproperlyConfigured from django.db.models import Model @@ -31,6 +32,13 @@ class PrefetchForIncludesHelperMixin(object): + + def __init__(self, *args, **kwargs): + warnings.warn("PrefetchForIncludesHelperMixin is deprecated. " + "Use SelectAndPrefetchForIncludesMixin instead", + DeprecationWarning) + super(PrefetchForIncludesHelperMixin, self).__init__(*args, **kwargs) + def get_queryset(self): """ This viewset provides a helper attribute to prefetch related models @@ -62,10 +70,52 @@ class MyViewSet(viewsets.ModelViewSet): return qs +class SelectAndPrefetchForIncludesMixin(object): + """ + This mixin provides a helper attributes to select or prefetch related models + based on the include specified in the URL. + + __all__ can be used to specify a prefetch which should be done regardless of the include + + .. code:: python + + # When MyViewSet is called with ?include=author it will prefetch author and authorbio + class MyViewSet(viewsets.ModelViewSet): + queryset = Book.objects.all() + prefetch_for_includes = { + '__all__': [], + 'category.section': ['category'] + } + select_for_includes = { + '__all__': [], + 'author': ['author', 'author__authorbio'], + } + """ + def get_queryset(self): + qs = super(SelectAndPrefetchForIncludesMixin, self).get_queryset() + + includes = self.request.GET.get('include', '').split(',') + ['__all__'] + + if hasattr(self, 'select_for_includes'): + selects = [self.select_for_includes.get(inc) for inc in includes] + qs = qs.select_related(*selects) + + if hasattr(self, 'prefetch_for_includes'): + prefetches = [self.prefetch_for_includes.get(inc) for inc in includes] + qs = qs.prefetch_related(*prefetches) + + return qs + + class AutoPrefetchMixin(object): def get_queryset(self, *args, **kwargs): """ This mixin adds automatic prefetching for OneToOne and ManyToMany fields. """ qs = super(AutoPrefetchMixin, self).get_queryset(*args, **kwargs) + + # Prefetch includes handled by another mixin, let's do not mix them + if hasattr(self, 'prefetch_for_includes'): + return qs + included_resources = get_included_resources(self.request) for included in included_resources: @@ -187,14 +237,14 @@ def get_related_instance(self): class ModelViewSet(AutoPrefetchMixin, - PrefetchForIncludesHelperMixin, + SelectAndPrefetchForIncludesMixin, RelatedMixin, viewsets.ModelViewSet): pass class ReadOnlyModelViewSet(AutoPrefetchMixin, - PrefetchForIncludesHelperMixin, + SelectAndPrefetchForIncludesMixin, RelatedMixin, viewsets.ReadOnlyModelViewSet): pass From d2d0b94f1850b26d96d231934100b8bf1e6ca599 Mon Sep 17 00:00:00 2001 From: Anton-Shutik Date: Mon, 25 Mar 2019 14:02:14 +0300 Subject: [PATCH 02/11] Renamed SelectAndPrefetchForIncludesMixin with PreloadIncludesMixin --- rest_framework_json_api/views.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rest_framework_json_api/views.py b/rest_framework_json_api/views.py index 0b6a3570..a87e414b 100644 --- a/rest_framework_json_api/views.py +++ b/rest_framework_json_api/views.py @@ -70,7 +70,7 @@ class MyViewSet(viewsets.ModelViewSet): return qs -class SelectAndPrefetchForIncludesMixin(object): +class PreloadIncludesMixin(object): """ This mixin provides a helper attributes to select or prefetch related models based on the include specified in the URL. @@ -92,7 +92,7 @@ class MyViewSet(viewsets.ModelViewSet): } """ def get_queryset(self): - qs = super(SelectAndPrefetchForIncludesMixin, self).get_queryset() + qs = super(PreloadIncludesMixin, self).get_queryset() includes = self.request.GET.get('include', '').split(',') + ['__all__'] @@ -237,14 +237,14 @@ def get_related_instance(self): class ModelViewSet(AutoPrefetchMixin, - SelectAndPrefetchForIncludesMixin, + PreloadIncludesMixin, RelatedMixin, viewsets.ModelViewSet): pass class ReadOnlyModelViewSet(AutoPrefetchMixin, - SelectAndPrefetchForIncludesMixin, + PreloadIncludesMixin, RelatedMixin, viewsets.ReadOnlyModelViewSet): pass From 9ae38485ebbf3677a2fff6b5a5eaa1ad01606ef4 Mon Sep 17 00:00:00 2001 From: Anton-Shutik Date: Mon, 25 Mar 2019 14:24:45 +0300 Subject: [PATCH 03/11] Removed check for "prefetch_for_includes" --- rest_framework_json_api/views.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/rest_framework_json_api/views.py b/rest_framework_json_api/views.py index a87e414b..5cd2e089 100644 --- a/rest_framework_json_api/views.py +++ b/rest_framework_json_api/views.py @@ -111,11 +111,6 @@ class AutoPrefetchMixin(object): def get_queryset(self, *args, **kwargs): """ This mixin adds automatic prefetching for OneToOne and ManyToMany fields. """ qs = super(AutoPrefetchMixin, self).get_queryset(*args, **kwargs) - - # Prefetch includes handled by another mixin, let's do not mix them - if hasattr(self, 'prefetch_for_includes'): - return qs - included_resources = get_included_resources(self.request) for included in included_resources: From 0ac3ef9aa5f9e05de6bcc593bb94c9c6d749e920 Mon Sep 17 00:00:00 2001 From: Anton-Shutik Date: Tue, 26 Mar 2019 19:07:52 +0300 Subject: [PATCH 04/11] Renamed mixins --- rest_framework_json_api/views.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/rest_framework_json_api/views.py b/rest_framework_json_api/views.py index 5cd2e089..1a997aae 100644 --- a/rest_framework_json_api/views.py +++ b/rest_framework_json_api/views.py @@ -35,7 +35,7 @@ class PrefetchForIncludesHelperMixin(object): def __init__(self, *args, **kwargs): warnings.warn("PrefetchForIncludesHelperMixin is deprecated. " - "Use SelectAndPrefetchForIncludesMixin instead", + "Use PreloadIncludesMixin instead", DeprecationWarning) super(PrefetchForIncludesHelperMixin, self).__init__(*args, **kwargs) @@ -107,10 +107,10 @@ def get_queryset(self): return qs -class AutoPrefetchMixin(object): +class AutoPreloadMixin(object): def get_queryset(self, *args, **kwargs): """ This mixin adds automatic prefetching for OneToOne and ManyToMany fields. """ - qs = super(AutoPrefetchMixin, self).get_queryset(*args, **kwargs) + qs = super(AutoPreloadMixin, self).get_queryset(*args, **kwargs) included_resources = get_included_resources(self.request) for included in included_resources: @@ -154,6 +154,15 @@ def get_queryset(self, *args, **kwargs): return qs +class AutoPrefetchMixin(AutoPreloadMixin): + + def __init__(self, *args, **kwargs): + warnings.warn("AutoPrefetchMixin is deprecated. " + "Use AutoPreloadMixin instead", + DeprecationWarning) + super(AutoPrefetchMixin, self).__init__(*args, **kwargs) + + class RelatedMixin(object): """ This mixin handles all related entities, whose Serializers are declared in "related_serializers" @@ -231,14 +240,14 @@ def get_related_instance(self): raise NotFound -class ModelViewSet(AutoPrefetchMixin, +class ModelViewSet(AutoPreloadMixin, PreloadIncludesMixin, RelatedMixin, viewsets.ModelViewSet): pass -class ReadOnlyModelViewSet(AutoPrefetchMixin, +class ReadOnlyModelViewSet(AutoPreloadMixin, PreloadIncludesMixin, RelatedMixin, viewsets.ReadOnlyModelViewSet): From 580e9a9b8d860f76b08b7b1828f65bcc9c2a79d2 Mon Sep 17 00:00:00 2001 From: Anton-Shutik Date: Fri, 17 May 2019 11:30:48 +0300 Subject: [PATCH 05/11] Merged PreloadIncludesMixin into AutoPreloadMixin, test --- example/tests/test_performance.py | 10 ++++++++ example/views.py | 3 +++ rest_framework_json_api/views.py | 38 +++++++++++++++---------------- 3 files changed, 32 insertions(+), 19 deletions(-) diff --git a/example/tests/test_performance.py b/example/tests/test_performance.py index 7da69bc9..50903498 100644 --- a/example/tests/test_performance.py +++ b/example/tests/test_performance.py @@ -56,3 +56,13 @@ def test_query_count_include_author(self): with self.assertNumQueries(5): response = self.client.get('/comments?include=author&page[size]=25') self.assertEqual(len(response.data['results']), 25) + + def test_query_select_related_entry(self): + """ We expect a list view with an include have three queries: + + 1. Primary resource COUNT query + 2. Primary resource SELECT + SELECT RELATED writer(author) and bio + """ + with self.assertNumQueries(2): + response = self.client.get('/comments?include=writer&page[size]=25') + self.assertEqual(len(response.data['results']), 25) diff --git a/example/views.py b/example/views.py index de6579bd..4d363032 100644 --- a/example/views.py +++ b/example/views.py @@ -184,6 +184,9 @@ class AuthorViewSet(ModelViewSet): class CommentViewSet(ModelViewSet): queryset = Comment.objects.all() serializer_class = CommentSerializer + select_for_includes = { + 'writer': ['author__bio'] + } prefetch_for_includes = { '__all__': [], 'author': ['author__bio', 'author__entries'], diff --git a/rest_framework_json_api/views.py b/rest_framework_json_api/views.py index 1a997aae..3f1a4acf 100644 --- a/rest_framework_json_api/views.py +++ b/rest_framework_json_api/views.py @@ -35,7 +35,7 @@ class PrefetchForIncludesHelperMixin(object): def __init__(self, *args, **kwargs): warnings.warn("PrefetchForIncludesHelperMixin is deprecated. " - "Use PreloadIncludesMixin instead", + "Use AutoPreloadMixin instead", DeprecationWarning) super(PrefetchForIncludesHelperMixin, self).__init__(*args, **kwargs) @@ -70,7 +70,7 @@ class MyViewSet(viewsets.ModelViewSet): return qs -class PreloadIncludesMixin(object): +class AutoPreloadMixin(object): """ This mixin provides a helper attributes to select or prefetch related models based on the include specified in the URL. @@ -91,29 +91,31 @@ class MyViewSet(viewsets.ModelViewSet): 'author': ['author', 'author__authorbio'], } """ - def get_queryset(self): - qs = super(PreloadIncludesMixin, self).get_queryset() - - includes = self.request.GET.get('include', '').split(',') + ['__all__'] - - if hasattr(self, 'select_for_includes'): - selects = [self.select_for_includes.get(inc) for inc in includes] - qs = qs.select_related(*selects) - if hasattr(self, 'prefetch_for_includes'): - prefetches = [self.prefetch_for_includes.get(inc) for inc in includes] - qs = qs.prefetch_related(*prefetches) - - return qs + def get_select_related(self, include): + return getattr(self, 'select_for_includes', {}).get(include, None) + def get_prefetch_related(self, include): + return getattr(self, 'prefetch_for_includes', {}).get(include, None) -class AutoPreloadMixin(object): def get_queryset(self, *args, **kwargs): """ This mixin adds automatic prefetching for OneToOne and ManyToMany fields. """ qs = super(AutoPreloadMixin, self).get_queryset(*args, **kwargs) included_resources = get_included_resources(self.request) - for included in included_resources: + for included in included_resources + ['__all__']: + # Custom defined "select_related" and "prefetch_related" is a priority + select_related = self.get_select_related(included) + if select_related is not None: + qs = qs.select_related(*select_related) + continue + + prefetch_related = self.get_prefetch_related(included) + if prefetch_related is not None: + qs = qs.prefetch_related(*prefetch_related) + continue + + # If include was not defined, trying to resolve it automatically included_model = None levels = included.split('.') level_model = qs.model @@ -241,14 +243,12 @@ def get_related_instance(self): class ModelViewSet(AutoPreloadMixin, - PreloadIncludesMixin, RelatedMixin, viewsets.ModelViewSet): pass class ReadOnlyModelViewSet(AutoPreloadMixin, - PreloadIncludesMixin, RelatedMixin, viewsets.ReadOnlyModelViewSet): pass From 6c9eac9afbcc1877f3eadc28cd02319094ce6670 Mon Sep 17 00:00:00 2001 From: Anton-Shutik Date: Fri, 17 May 2019 14:27:39 +0300 Subject: [PATCH 06/11] Use select_related if possible --- example/tests/test_performance.py | 2 +- rest_framework_json_api/views.py | 19 ++++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/example/tests/test_performance.py b/example/tests/test_performance.py index 50903498..ac8b5956 100644 --- a/example/tests/test_performance.py +++ b/example/tests/test_performance.py @@ -58,7 +58,7 @@ def test_query_count_include_author(self): self.assertEqual(len(response.data['results']), 25) def test_query_select_related_entry(self): - """ We expect a list view with an include have three queries: + """ We expect a list view with an include have two queries: 1. Primary resource COUNT query 2. Primary resource SELECT + SELECT RELATED writer(author) and bio diff --git a/rest_framework_json_api/views.py b/rest_framework_json_api/views.py index 3f1a4acf..d107ed7b 100644 --- a/rest_framework_json_api/views.py +++ b/rest_framework_json_api/views.py @@ -119,6 +119,8 @@ def get_queryset(self, *args, **kwargs): included_model = None levels = included.split('.') level_model = qs.model + # Suppose we can do select_related by default + can_select_related = True for level in levels: if not hasattr(level_model, level): break @@ -126,16 +128,20 @@ def get_queryset(self, *args, **kwargs): field_class = field.__class__ is_forward_relation = ( - issubclass(field_class, ForwardManyToOneDescriptor) or - issubclass(field_class, ManyToManyDescriptor) + issubclass(field_class, (ForwardManyToOneDescriptor, ManyToManyDescriptor)) ) is_reverse_relation = ( - issubclass(field_class, ReverseManyToOneDescriptor) or - issubclass(field_class, ReverseOneToOneDescriptor) + issubclass(field_class, (ReverseManyToOneDescriptor, ReverseOneToOneDescriptor)) ) if not (is_forward_relation or is_reverse_relation): break + # Figuring out if relation should be select related rather than prefetch_related + # If at least one relation in the chain is not "selectable" then use "prefetch" + can_select_related &= ( + issubclass(field_class, (ForwardManyToOneDescriptor, ReverseOneToOneDescriptor)) + ) + if level == levels[-1]: included_model = field else: @@ -151,7 +157,10 @@ def get_queryset(self, *args, **kwargs): level_model = model_field.model if included_model is not None: - qs = qs.prefetch_related(included.replace('.', '__')) + if can_select_related: + qs = qs.select_related(included.replace('.', '__')) + else: + qs = qs.prefetch_related(included.replace('.', '__')) return qs From 5ab68a9df57a7bcd9f2f9d1edb21328998dd1fed Mon Sep 17 00:00:00 2001 From: Anton-Shutik Date: Fri, 17 May 2019 15:26:50 +0300 Subject: [PATCH 07/11] Polymorphic FK should be explicitly overridden --- example/views.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/example/views.py b/example/views.py index 4d363032..891957ff 100644 --- a/example/views.py +++ b/example/views.py @@ -203,6 +203,9 @@ def get_queryset(self, *args, **kwargs): class CompanyViewset(ModelViewSet): queryset = Company.objects.all() serializer_class = CompanySerializer + prefetch_for_includes = { + 'current_project': ['current_project'], + } class ProjectViewset(ModelViewSet): From 3422186281adbb5c8b85a06c301f4e6fe44acebd Mon Sep 17 00:00:00 2001 From: Anton-Shutik Date: Fri, 17 May 2019 16:07:27 +0300 Subject: [PATCH 08/11] Updated docs --- docs/usage.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/usage.md b/docs/usage.md index 0531daa3..fd32e1da 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -823,7 +823,9 @@ class QuestSerializer(serializers.ModelSerializer): Be aware that using included resources without any form of prefetching **WILL HURT PERFORMANCE** as it will introduce m\*(n+1) queries. -A viewset helper was designed to allow for greater flexibility and it is automatically available when subclassing +A viewset helper was designed to allow for greater flexibility and it is automatically available when subclassing. +You can also define your custom queryset for `select` or `prefetch` related for each `include` that comes from the url. +It has a priority over automatically added preloads. `rest_framework_json_api.views.ModelViewSet`: ```python from rest_framework_json_api import views @@ -831,9 +833,12 @@ from rest_framework_json_api import views # When MyViewSet is called with ?include=author it will dynamically prefetch author and author.bio class MyViewSet(views.ModelViewSet): queryset = Book.objects.all() + select_for_includes = { + 'author': ['author__bio'], + } prefetch_for_includes = { '__all__': [], - 'author': ['author', 'author__bio'], + 'all_authors': [Prefetch('all_authors', queryset=Author.objects.select_related('bio'))], 'category.section': ['category'] } ``` From b73c08b7fe313ced6807cd23588555dea52f2de6 Mon Sep 17 00:00:00 2001 From: Anton-Shutik Date: Fri, 17 May 2019 16:11:44 +0300 Subject: [PATCH 09/11] Updated change log --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf1b1723..11c06bc2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ any parts of the framework not mentioned in the documentation should generally b ### Fixed +* Added `select_for_includes` handling. [PR](https://github.com/django-json-api/django-rest-framework-json-api/pull/600). [Docs](https://django-rest-framework-json-api.readthedocs.io/en/stable/usage.html#performance-improvements). * Avoid exception when trying to include skipped relationship * Don't swallow `filter[]` params when there are several * Fix DeprecationWarning regarding collections.abc import in Python 3.7 From 36cb9988c2a317310314f9eb1924ba89773f476f Mon Sep 17 00:00:00 2001 From: Anton-Shutik Date: Fri, 24 May 2019 15:40:22 +0300 Subject: [PATCH 10/11] Refactored AutoPrefetchRelated mixin --- example/tests/test_performance.py | 2 +- example/views.py | 4 ++-- rest_framework_json_api/views.py | 25 +++++++++++++++++-------- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/example/tests/test_performance.py b/example/tests/test_performance.py index ac8b5956..45c71aeb 100644 --- a/example/tests/test_performance.py +++ b/example/tests/test_performance.py @@ -53,7 +53,7 @@ def test_query_count_include_author(self): 4. Author types prefetched 5. Entries prefetched """ - with self.assertNumQueries(5): + with self.assertNumQueries(4): response = self.client.get('/comments?include=author&page[size]=25') self.assertEqual(len(response.data['results']), 25) diff --git a/example/views.py b/example/views.py index 891957ff..73a9136e 100644 --- a/example/views.py +++ b/example/views.py @@ -12,7 +12,7 @@ from rest_framework_json_api.filters import OrderingFilter, QueryParameterValidationFilter from rest_framework_json_api.pagination import JsonApiPageNumberPagination from rest_framework_json_api.utils import format_drf_errors -from rest_framework_json_api.views import ModelViewSet, RelationshipView +from rest_framework_json_api.views import ModelViewSet, RelationshipView, PreloadIncludesMixin from example.models import Author, Blog, Comment, Company, Entry, Project, ProjectType from example.serializers import ( @@ -200,7 +200,7 @@ def get_queryset(self, *args, **kwargs): return super(CommentViewSet, self).get_queryset() -class CompanyViewset(ModelViewSet): +class CompanyViewset(PreloadIncludesMixin, viewsets.ModelViewSet): queryset = Company.objects.all() serializer_class = CompanySerializer prefetch_for_includes = { diff --git a/rest_framework_json_api/views.py b/rest_framework_json_api/views.py index d107ed7b..a7a125f9 100644 --- a/rest_framework_json_api/views.py +++ b/rest_framework_json_api/views.py @@ -35,7 +35,7 @@ class PrefetchForIncludesHelperMixin(object): def __init__(self, *args, **kwargs): warnings.warn("PrefetchForIncludesHelperMixin is deprecated. " - "Use AutoPreloadMixin instead", + "Use PreloadIncludesMixin instead", DeprecationWarning) super(PrefetchForIncludesHelperMixin, self).__init__(*args, **kwargs) @@ -70,7 +70,7 @@ class MyViewSet(viewsets.ModelViewSet): return qs -class AutoPreloadMixin(object): +class PreloadIncludesMixin(object): """ This mixin provides a helper attributes to select or prefetch related models based on the include specified in the URL. @@ -99,22 +99,30 @@ def get_prefetch_related(self, include): return getattr(self, 'prefetch_for_includes', {}).get(include, None) def get_queryset(self, *args, **kwargs): - """ This mixin adds automatic prefetching for OneToOne and ManyToMany fields. """ - qs = super(AutoPreloadMixin, self).get_queryset(*args, **kwargs) - included_resources = get_included_resources(self.request) + qs = super(PreloadIncludesMixin, self).get_queryset(*args, **kwargs) + included_resources = get_included_resources(self.request) for included in included_resources + ['__all__']: - # Custom defined "select_related" and "prefetch_related" is a priority + select_related = self.get_select_related(included) if select_related is not None: qs = qs.select_related(*select_related) - continue prefetch_related = self.get_prefetch_related(included) if prefetch_related is not None: qs = qs.prefetch_related(*prefetch_related) - continue + return qs + + +class AutoPreloadMixin(object): + + def get_queryset(self, *args, **kwargs): + """ This mixin adds automatic prefetching for OneToOne and ManyToMany fields. """ + qs = super(AutoPreloadMixin, self).get_queryset(*args, **kwargs) + included_resources = get_included_resources(self.request) + + for included in included_resources + ['__all__']: # If include was not defined, trying to resolve it automatically included_model = None levels = included.split('.') @@ -252,6 +260,7 @@ def get_related_instance(self): class ModelViewSet(AutoPreloadMixin, + PreloadIncludesMixin, RelatedMixin, viewsets.ModelViewSet): pass From db5251de4f141b68932b9f3e58a7b886515710d5 Mon Sep 17 00:00:00 2001 From: Oliver Sauder Date: Tue, 28 May 2019 16:31:01 +0200 Subject: [PATCH 11/11] Clarify changelog and documentation --- CHANGELOG.md | 13 +++++++++++-- docs/usage.md | 13 +++++++------ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11c06bc2..c5d5e074 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,13 +14,22 @@ any parts of the framework not mentioned in the documentation should generally b * Add support for Django 2.2 +### Changed + +* Allow to define `select_related` per include using [select_for_includes](https://django-rest-framework-json-api.readthedocs.io/en/stable/usage.html#performance-improvements) +* Reduce number of queries to calculate includes by using `select_related` when possible + ### Fixed -* Added `select_for_includes` handling. [PR](https://github.com/django-json-api/django-rest-framework-json-api/pull/600). [Docs](https://django-rest-framework-json-api.readthedocs.io/en/stable/usage.html#performance-improvements). * Avoid exception when trying to include skipped relationship * Don't swallow `filter[]` params when there are several * Fix DeprecationWarning regarding collections.abc import in Python 3.7 -* Allow OPTIONS request to be used on RelationshipView. +* Allow OPTIONS request to be used on RelationshipView + +### Deprecated + +* Deprecate `PrefetchForIncludesHelperMixin` use `PreloadIncludesMixin` instead +* Deprecate `AutoPrefetchMixin` use `AutoPreloadMixin` instead ## [2.7.0] - 2019-01-14 diff --git a/docs/usage.md b/docs/usage.md index fd32e1da..c06d276c 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -823,9 +823,10 @@ class QuestSerializer(serializers.ModelSerializer): Be aware that using included resources without any form of prefetching **WILL HURT PERFORMANCE** as it will introduce m\*(n+1) queries. -A viewset helper was designed to allow for greater flexibility and it is automatically available when subclassing. -You can also define your custom queryset for `select` or `prefetch` related for each `include` that comes from the url. -It has a priority over automatically added preloads. +A viewset helper was therefore designed to automatically preload data when possible. Such is automatically available when subclassing `ModelViewSet`. + +It also allows to define custom `select_related` and `prefetch_related` for each requested `include` when needed in special cases: + `rest_framework_json_api.views.ModelViewSet`: ```python from rest_framework_json_api import views @@ -853,7 +854,7 @@ class MyReadOnlyViewSet(views.ReadOnlyModelViewSet): 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. -Using the helper to prefetch, rather than attempting to minimise queries via select_related might give you better performance depending on the characteristics of your data and database. +Using the helper to prefetch, rather than attempting to minimise queries via `select_related` might give you better performance depending on the characteristics of your data and database. For example: @@ -866,11 +867,11 @@ a) 1 query via selected_related, e.g. SELECT * FROM books LEFT JOIN author LEFT b) 4 small queries via prefetch_related. If you have 1M books, 50k authors, 10k categories, 10k copyrightholders -in the select_related scenario, you've just created a in-memory table +in the `select_related` scenario, you've just created a in-memory table with 1e18 rows which will likely exhaust any available memory and slow your database to crawl. -The prefetch_related case will issue 4 queries, but they will be small and fast queries. +The `prefetch_related` case will issue 4 queries, but they will be small and fast queries.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy