mixins.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. from __future__ import print_function, unicode_literals
  2. import traceback
  3. from rest_framework import status
  4. from rest_framework.mixins import CreateModelMixin
  5. from rest_framework.response import Response
  6. __all__ = [
  7. 'BulkCreateModelMixin',
  8. 'BulkDestroyModelMixin',
  9. 'BulkUpdateModelMixin',
  10. ]
  11. class BulkCreateModelMixin(CreateModelMixin):
  12. """
  13. Either create a single or many model instances in bulk by using the
  14. Serializers ``many=True`` ability from Django REST >= 2.2.5.
  15. .. note::
  16. This mixin uses the same method to create model instances
  17. as ``CreateModelMixin`` because both non-bulk and bulk
  18. requests will use ``POST`` request method.
  19. """
  20. def create(self, request, *args, **kwargs):
  21. bulk = isinstance(request.data, list)
  22. if not bulk:
  23. return super(BulkCreateModelMixin, self).create(request, *args, **kwargs)
  24. else:
  25. serializer = self.get_serializer(data=request.data, many=True)
  26. serializer.is_valid(raise_exception=True)
  27. self.perform_bulk_create(serializer)
  28. return Response(serializer.data, status=status.HTTP_201_CREATED)
  29. def perform_bulk_create(self, serializer):
  30. return self.perform_create(serializer)
  31. class BulkUpdateModelMixin(object):
  32. """
  33. Update model instances in bulk by using the Serializers
  34. ``many=True`` ability from Django REST >= 2.2.5.
  35. """
  36. def get_object(self):
  37. try:
  38. return super(BulkUpdateModelMixin, self).get_object()
  39. except AssertionError:
  40. # probably happened when called get_object() within options()
  41. # via self.metadata_class which is not allowed on list viewset
  42. # however since we are enabling PUT here, we should handle the
  43. # exception if called within options()
  44. # We can simply swallow the exception since that method
  45. # does not actually do anything with the returned object
  46. for file, line, function, code in traceback.extract_stack():
  47. if all((file.endswith('rest_framework/views.py'),
  48. function == 'options')):
  49. return
  50. # not called inside metadata() so probably something went
  51. # wrong and so we should reraise exception
  52. raise
  53. def bulk_update(self, request, *args, **kwargs):
  54. partial = kwargs.pop('partial', False)
  55. # restrict the update to the filtered queryset
  56. serializer = self.get_serializer(
  57. self.filter_queryset(self.get_queryset()),
  58. data=request.data,
  59. many=True,
  60. partial=partial,
  61. )
  62. serializer.is_valid(raise_exception=True)
  63. self.perform_bulk_update(serializer)
  64. return Response(serializer.data, status=status.HTTP_200_OK)
  65. def partial_bulk_update(self, request, *args, **kwargs):
  66. kwargs['partial'] = True
  67. return self.bulk_update(request, *args, **kwargs)
  68. def perform_update(self, serializer):
  69. serializer.save()
  70. def perform_bulk_update(self, serializer):
  71. return self.perform_update(serializer)
  72. class BulkDestroyModelMixin(object):
  73. """
  74. Destroy model instances.
  75. """
  76. def allow_bulk_destroy(self, qs, filtered):
  77. """
  78. Hook to ensure that the bulk destroy should be allowed.
  79. By default this checks that the destroy is only applied to
  80. filtered querysets.
  81. """
  82. return qs is not filtered
  83. def bulk_destroy(self, request, *args, **kwargs):
  84. qs = self.get_queryset()
  85. filtered = self.filter_queryset(qs)
  86. if not self.allow_bulk_destroy(qs, filtered):
  87. return Response(status=status.HTTP_400_BAD_REQUEST)
  88. self.perform_bulk_destroy(filtered)
  89. return Response(status=status.HTTP_204_NO_CONTENT)
  90. def perform_destroy(self, instance):
  91. instance.delete()
  92. def perform_bulk_destroy(self, objects):
  93. for obj in objects:
  94. self.perform_destroy(obj)