88 lines
3.4 KiB
Python
88 lines
3.4 KiB
Python
from rest_framework import viewsets
|
|
from rest_framework_json_api import serializers
|
|
from rest_framework_json_api.utils import get_included_resources
|
|
from rest_framework_json_api.views import AutoPrefetchMixin, RelatedMixin
|
|
|
|
basic_filter = ('exact', 'isnull')
|
|
text_filter = ('exact', 'contains', 'iexact', 'icontains', 'startswith', 'istartswith', 'endswith', 'iendswith')
|
|
date_filter = ('exact', 'gte', 'lte')
|
|
int_filter = ('exact', 'gte', 'lte')
|
|
|
|
|
|
class PreloadIncludesMixin(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_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)
|
|
|
|
def get_queryset(self, *args, **kwargs):
|
|
qs = super(PreloadIncludesMixin, self).get_queryset(*args, **kwargs)
|
|
|
|
included_resources = get_included_resources(self.request)
|
|
for included in included_resources + ['__all__']:
|
|
|
|
select_related = self.get_select_related(included)
|
|
if select_related is not None:
|
|
qs = qs.select_related(*select_related)
|
|
|
|
prefetch_related = self.get_prefetch_related(included)
|
|
if prefetch_related is not None:
|
|
if not isinstance(prefetch_related, list):
|
|
qs = qs.prefetch_related(*prefetch_related(self))
|
|
else:
|
|
qs = qs.prefetch_related(*prefetch_related)
|
|
|
|
return qs
|
|
|
|
|
|
class ReasonableModelViewSet(AutoPrefetchMixin,
|
|
PreloadIncludesMixin,
|
|
RelatedMixin,
|
|
viewsets.ModelViewSet):
|
|
http_method_names = ['get', 'post', 'patch', 'delete', 'head', 'options']
|
|
|
|
|
|
class ReasonableModelSerializer(serializers.ModelSerializer):
|
|
"""
|
|
Reusable class to extend DJA ModelSerializer and make it more JSONAPI compatible
|
|
"""
|
|
|
|
def get_field_names(self, declared_fields, info):
|
|
method = self.context['request'].method
|
|
|
|
included_rel_fields = []
|
|
if method == 'GET':
|
|
included_rel_fields = [qp for qp in (
|
|
self.context['request'].query_params['include'].split(',')
|
|
if 'include' in self.context['request'].query_params else []
|
|
) if qp in (self.included_serializers if hasattr(self, 'included_serializers') else [])]
|
|
elif method == 'POST' or method == 'PATCH':
|
|
included_rel_fields = [irel for irel in (
|
|
self.context['request'].data.keys()
|
|
) if irel in (self.included_serializers if hasattr(self, 'included_serializers') else [])]
|
|
|
|
fields = super().get_field_names(declared_fields, info)
|
|
return fields + included_rel_fields |