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