|
| 1 | +import warnings |
1 | 2 | from collections import Iterable
|
2 | 3 |
|
3 | 4 | from django.core.exceptions import ImproperlyConfigured
|
|
31 | 32 |
|
32 | 33 |
|
33 | 34 | class PrefetchForIncludesHelperMixin(object):
|
| 35 | + |
| 36 | + def __init__(self, *args, **kwargs): |
| 37 | + warnings.warn("PrefetchForIncludesHelperMixin is deprecated. " |
| 38 | + "Use SelectAndPrefetchForIncludesMixin instead", |
| 39 | + DeprecationWarning) |
| 40 | + super(PrefetchForIncludesHelperMixin, self).__init__(*args, **kwargs) |
| 41 | + |
34 | 42 | def get_queryset(self):
|
35 | 43 | """
|
36 | 44 | This viewset provides a helper attribute to prefetch related models
|
@@ -62,10 +70,52 @@ class MyViewSet(viewsets.ModelViewSet):
|
62 | 70 | return qs
|
63 | 71 |
|
64 | 72 |
|
| 73 | +class SelectAndPrefetchForIncludesMixin(object): |
| 74 | + """ |
| 75 | + This mixin provides a helper attributes to select or prefetch related models |
| 76 | + based on the include specified in the URL. |
| 77 | +
|
| 78 | + __all__ can be used to specify a prefetch which should be done regardless of the include |
| 79 | +
|
| 80 | + .. code:: python |
| 81 | +
|
| 82 | + # When MyViewSet is called with ?include=author it will prefetch author and authorbio |
| 83 | + class MyViewSet(viewsets.ModelViewSet): |
| 84 | + queryset = Book.objects.all() |
| 85 | + prefetch_for_includes = { |
| 86 | + '__all__': [], |
| 87 | + 'category.section': ['category'] |
| 88 | + } |
| 89 | + select_for_includes = { |
| 90 | + '__all__': [], |
| 91 | + 'author': ['author', 'author__authorbio'], |
| 92 | + } |
| 93 | + """ |
| 94 | + def get_queryset(self): |
| 95 | + qs = super(SelectAndPrefetchForIncludesMixin, self).get_queryset() |
| 96 | + |
| 97 | + includes = self.request.GET.get('include', '').split(',') + ['__all__'] |
| 98 | + |
| 99 | + if hasattr(self, 'select_for_includes'): |
| 100 | + selects = [self.select_for_includes.get(inc) for inc in includes] |
| 101 | + qs = qs.select_related(*selects) |
| 102 | + |
| 103 | + if hasattr(self, 'prefetch_for_includes'): |
| 104 | + prefetches = [self.prefetch_for_includes.get(inc) for inc in includes] |
| 105 | + qs = qs.prefetch_related(*prefetches) |
| 106 | + |
| 107 | + return qs |
| 108 | + |
| 109 | + |
65 | 110 | class AutoPrefetchMixin(object):
|
66 | 111 | def get_queryset(self, *args, **kwargs):
|
67 | 112 | """ This mixin adds automatic prefetching for OneToOne and ManyToMany fields. """
|
68 | 113 | qs = super(AutoPrefetchMixin, self).get_queryset(*args, **kwargs)
|
| 114 | + |
| 115 | + # Prefetch includes handled by another mixin, let's do not mix them |
| 116 | + if hasattr(self, 'prefetch_for_includes'): |
| 117 | + return qs |
| 118 | + |
69 | 119 | included_resources = get_included_resources(self.request)
|
70 | 120 |
|
71 | 121 | for included in included_resources:
|
@@ -187,14 +237,14 @@ def get_related_instance(self):
|
187 | 237 |
|
188 | 238 |
|
189 | 239 | class ModelViewSet(AutoPrefetchMixin,
|
190 |
| - PrefetchForIncludesHelperMixin, |
| 240 | + SelectAndPrefetchForIncludesMixin, |
191 | 241 | RelatedMixin,
|
192 | 242 | viewsets.ModelViewSet):
|
193 | 243 | pass
|
194 | 244 |
|
195 | 245 |
|
196 | 246 | class ReadOnlyModelViewSet(AutoPrefetchMixin,
|
197 |
| - PrefetchForIncludesHelperMixin, |
| 247 | + SelectAndPrefetchForIncludesMixin, |
198 | 248 | RelatedMixin,
|
199 | 249 | viewsets.ReadOnlyModelViewSet):
|
200 | 250 | pass
|
|
0 commit comments