README.rst 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. Django REST Framework Bulk
  2. ==========================
  3. .. image:: https://badge.fury.io/py/djangorestframework-bulk.png
  4. :target: http://badge.fury.io/py/djangorestframework-bulk
  5. .. image:: https://travis-ci.org/miki725/django-rest-framework-bulk.svg?branch=master
  6. :target: https://travis-ci.org/miki725/django-rest-framework-bulk
  7. Django REST Framework bulk CRUD view mixins.
  8. Overview
  9. --------
  10. Django REST Framework comes with many generic views however none
  11. of them allow to do bulk operations such as create, update and delete.
  12. To keep the core of Django REST Framework simple, its maintainer
  13. suggested to create a separate project to allow for bulk operations
  14. within the framework. That is the purpose of this project.
  15. Requirements
  16. ------------
  17. * Python 2.7+
  18. * Django 1.3+
  19. * Django REST Framework >= 2.2.5 (when bulk features were added to serializers)
  20. * Django REST Framework >= 3.0.0 (DRF-bulk supports both DRF2 and DRF3!)
  21. Installing
  22. ----------
  23. Using pip::
  24. $ pip install djangorestframework-bulk
  25. or from source code::
  26. $ pip install -e git+http://github.com/miki725/django-rest-framework-bulk#egg=djangorestframework-bulk
  27. Example
  28. -------
  29. The bulk views (and mixins) are very similar to Django REST Framework's own
  30. generic views (and mixins)::
  31. from rest_framework_bulk import (
  32. BulkListSerializer,
  33. BulkSerializerMixin,
  34. ListBulkCreateUpdateDestroyAPIView,
  35. )
  36. class FooSerializer(BulkSerializerMixin, ModelSerializer):
  37. class Meta(object):
  38. model = FooModel
  39. # only necessary in DRF3
  40. list_serializer_class = BulkListSerializer
  41. class FooView(ListBulkCreateUpdateDestroyAPIView):
  42. queryset = FooModel.objects.all()
  43. serializer_class = FooSerializer
  44. The above will allow to create the following queries
  45. ::
  46. # list queryset
  47. GET
  48. ::
  49. # create single resource
  50. POST
  51. {"field":"value","field2":"value2"} <- json object in request data
  52. ::
  53. # create multiple resources
  54. POST
  55. [{"field":"value","field2":"value2"}]
  56. ::
  57. # update multiple resources (requires all fields)
  58. PUT
  59. [{"field":"value","field2":"value2"}] <- json list of objects in data
  60. ::
  61. # partial update multiple resources
  62. PATCH
  63. [{"field":"value"}] <- json list of objects in data
  64. ::
  65. # delete queryset (see notes)
  66. DELETE
  67. Router
  68. ------
  69. The bulk router can automatically map the bulk actions::
  70. from rest_framework_bulk.routes import BulkRouter
  71. class UserViewSet(BulkModelViewSet):
  72. model = User
  73. def allow_bulk_destroy(self, qs, filtered):
  74. """Don't forget to fine-grain this method"""
  75. router = BulkRouter()
  76. router.register(r'users', UserViewSet)
  77. DRF3
  78. ----
  79. Django REST Framework made many API changes which included major changes
  80. in serializers. As a result, please note the following in order to use
  81. DRF-bulk with DRF3:
  82. * You must specify custom ``list_serializer_class`` if your view(set)
  83. will require update functionality (when using ``BulkUpdateModelMixin``)
  84. * DRF3 removes read-only fields from ``serializer.validated_data``.
  85. As a result, it is impossible to correlate each ``validated_data``
  86. in ``ListSerializer`` with a model instance to update since ``validated_data``
  87. will be missing the model primary key since that is a read-only field.
  88. To deal with that, you must use ``BulkSerializerMixin`` mixin in your serializer
  89. class which will add the model primary key field back to the ``validated_data``.
  90. By default ``id`` field is used however you can customize that field
  91. by using ``update_lookup_field`` in the serializers ``Meta``::
  92. class FooSerializer(BulkSerializerMixin, ModelSerializer):
  93. class Meta(object):
  94. model = FooModel
  95. list_serializer_class = BulkListSerializer
  96. update_lookup_field = 'slug'
  97. Notes
  98. -----
  99. Most API urls have two URL levels for each resource:
  100. 1. ``url(r'foo/', ...)``
  101. 2. ``url(r'foo/(?P<pk>\d+)/', ...)``
  102. The second url however is not applicable for bulk operations because
  103. the url directly maps to a single resource. Therefore all bulk
  104. generic views only apply to the first url.
  105. There are multiple generic view classes in case only a certail
  106. bulk functionality is required. For example ``ListBulkCreateAPIView``
  107. will only do bulk operations for creating resources.
  108. For a complete list of available generic view classes, please
  109. take a look at the source code at ``generics.py`` as it is mostly
  110. self-explanatory.
  111. Most bulk operations are pretty safe in terms of how they operate,
  112. that is you explicitly describe all requests. For example, if you
  113. need to update 3 specific resources, you have to explicitly identify
  114. those resources in the request's ``PUT`` or ``PATCH`` data.
  115. The only exception to this is bulk delete. Consider a ``DELETE``
  116. request to the first url. That can potentially delete all resources
  117. without any special confirmation. To try to account for this, bulk delete
  118. mixin allows to implement a hook to determine if the bulk delete
  119. request should be allowed::
  120. class FooView(BulkDestroyAPIView):
  121. def allow_bulk_destroy(self, qs, filtered):
  122. # custom logic here
  123. # default checks if the qs was filtered
  124. # qs comes from self.get_queryset()
  125. # filtered comes from self.filter_queryset(qs)
  126. return qs is not filtered
  127. By default it checks if the queryset was filtered and if not will not
  128. allow the bulk delete to complete. The logic here is that if the request
  129. is filtered to only get certain resources, more attention was payed hence
  130. the action is less likely to be accidental. On how to filter requests,
  131. please refer to Django REST
  132. `docs <http://www.django-rest-framework.org/api-guide/filtering>`_.
  133. Either way, please use bulk deletes with extreme caution since they
  134. can be dangerous.