| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124 | from __future__ import unicode_literals, print_functionimport tracebackfrom django.core.exceptions import ImproperlyConfigured, ValidationErrorfrom rest_framework import statusfrom rest_framework.mixins import CreateModelMixinfrom rest_framework.response import Response__all__ = [    'BulkCreateModelMixin',    'BulkDestroyModelMixin',    'BulkUpdateModelMixin',]class BulkCreateModelMixin(CreateModelMixin):    """    Either create a single or many model instances in bulk by using the    Serializers ``many=True`` ability from Django REST >= 2.2.5.    .. note::        This mixin uses the same method to create model instances        as ``CreateModelMixin`` because both non-bulk and bulk        requests will use ``POST`` request method.    """    def create(self, request, *args, **kwargs):        bulk = isinstance(request.DATA, list)        if not bulk:            return super(BulkCreateModelMixin, self).create(request, *args, **kwargs)        else:            serializer = self.get_serializer(data=request.DATA, many=True)            if serializer.is_valid():                for obj in serializer.object:                    self.pre_save(obj)                self.object = serializer.save(force_insert=True)                for obj in self.object:                    self.post_save(obj, created=True)                return Response(serializer.data, status=status.HTTP_201_CREATED)        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)class BulkUpdateModelMixin(object):    """    Update model instances in bulk by using the Serializers    ``many=True`` ability from Django REST >= 2.2.5.    """    def get_object(self):        try:            return super(BulkUpdateModelMixin, self).get_object()        except ImproperlyConfigured:            # probably happened when called get_object() within metadata()            # which is not allowed on list viewset however since we are enabling            # PUT here, we should handle the exception            # if called within metadata(), we can simply swallow exception            # since that method does not actually do anything            # with the returned object            for file, line, function, code in traceback.extract_stack():                if all((file.endswith('rest_framework/generics.py'),                        function == 'metadata')):                    return            # not called inside metadata() so probably something went            # wrong and so we should reraise exception            raise    def bulk_update(self, request, *args, **kwargs):        partial = kwargs.pop('partial', False)        # restrict the update to the filtered queryset        serializer = self.get_serializer(self.filter_queryset(self.get_queryset()),                                         data=request.DATA,                                         many=True,                                         partial=partial)        if serializer.is_valid():            try:                for obj in serializer.object:                    self.pre_save(obj)            except ValidationError as err:                # full_clean on model instances may be called in pre_save                # so we have to handle eventual errors.                return Response(err.message_dict, status=status.HTTP_400_BAD_REQUEST)            self.object = serializer.save(force_update=True)            for obj in self.object:                self.post_save(obj, created=False)            return Response(serializer.data, status=status.HTTP_200_OK)        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)    def partial_bulk_update(self, request, *args, **kwargs):        kwargs['partial'] = True        return self.bulk_update(request, *args, **kwargs)class BulkDestroyModelMixin(object):    """    Destroy model instances.    """    def allow_bulk_destroy(self, qs, filtered):        """        Hook to ensure that the bulk destroy should be allowed.        By default this checks that the destroy is only applied to        filtered querysets.        """        return qs is not filtered    def bulk_destroy(self, request, *args, **kwargs):        qs = self.get_queryset()        filtered = self.filter_queryset(qs)        if not self.allow_bulk_destroy(qs, filtered):            return Response(status=status.HTTP_400_BAD_REQUEST)        for obj in filtered:            self.pre_delete(obj)            obj.delete()            self.post_delete(obj)        return Response(status=status.HTTP_204_NO_CONTENT)
 |