mixins.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. from __future__ import print_function, unicode_literals
  2. from rest_framework import status
  3. from rest_framework.mixins import CreateModelMixin
  4. from rest_framework.response import Response
  5. __all__ = [
  6. 'BulkCreateModelMixin',
  7. 'BulkDestroyModelMixin',
  8. 'BulkUpdateModelMixin',
  9. ]
  10. class BulkCreateModelMixin(CreateModelMixin):
  11. """
  12. Either create a single or many model instances in bulk by using the
  13. Serializers ``many=True`` ability from Django REST >= 2.2.5.
  14. .. note::
  15. This mixin uses the same method to create model instances
  16. as ``CreateModelMixin`` because both non-bulk and bulk
  17. requests will use ``POST`` request method.
  18. """
  19. def create(self, request, *args, **kwargs):
  20. bulk = isinstance(request.data, list)
  21. if not bulk:
  22. return super(BulkCreateModelMixin, self).create(request, *args, **kwargs)
  23. else:
  24. serializer = self.get_serializer(data=request.data, many=True)
  25. serializer.is_valid(raise_exception=True)
  26. self.perform_bulk_create(serializer)
  27. return Response(serializer.data, status=status.HTTP_201_CREATED)
  28. def perform_bulk_create(self, serializer):
  29. return self.perform_create(serializer)
  30. class BulkUpdateModelMixin(object):
  31. """
  32. Update model instances in bulk by using the Serializers
  33. ``many=True`` ability from Django REST >= 2.2.5.
  34. """
  35. def get_object(self):
  36. lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
  37. if lookup_url_kwarg in self.kwargs:
  38. return super(BulkUpdateModelMixin, self).get_object()
  39. # If the lookup_url_kwarg is not present
  40. # get_object() is most likely called as part of options()
  41. # which by default simply checks for object permissions
  42. # and raises permission denied if necessary.
  43. # Here we don't need to check for general permissions
  44. # and can simply return None since general permissions
  45. # are checked in initial() which always gets executed
  46. # before any of the API actions (e.g. create, update, etc)
  47. return
  48. def bulk_update(self, request, *args, **kwargs):
  49. partial = kwargs.pop('partial', False)
  50. # restrict the update to the filtered queryset
  51. serializer = self.get_serializer(
  52. self.filter_queryset(self.get_queryset()),
  53. data=request.data,
  54. many=True,
  55. partial=partial,
  56. )
  57. serializer.is_valid(raise_exception=True)
  58. self.perform_bulk_update(serializer)
  59. return Response(serializer.data, status=status.HTTP_200_OK)
  60. def partial_bulk_update(self, request, *args, **kwargs):
  61. kwargs['partial'] = True
  62. return self.bulk_update(request, *args, **kwargs)
  63. def perform_update(self, serializer):
  64. serializer.save()
  65. def perform_bulk_update(self, serializer):
  66. return self.perform_update(serializer)
  67. class BulkDestroyModelMixin(object):
  68. """
  69. Destroy model instances.
  70. """
  71. def allow_bulk_destroy(self, qs, filtered):
  72. """
  73. Hook to ensure that the bulk destroy should be allowed.
  74. By default this checks that the destroy is only applied to
  75. filtered querysets.
  76. """
  77. return qs is not filtered
  78. def bulk_destroy(self, request, *args, **kwargs):
  79. qs = self.get_queryset()
  80. filtered = self.filter_queryset(qs)
  81. if not self.allow_bulk_destroy(qs, filtered):
  82. return Response(status=status.HTTP_400_BAD_REQUEST)
  83. self.perform_bulk_destroy(filtered)
  84. return Response(status=status.HTTP_204_NO_CONTENT)
  85. def perform_destroy(self, instance):
  86. instance.delete()
  87. def perform_bulk_destroy(self, objects):
  88. for obj in objects:
  89. self.perform_destroy(obj)