by miki725

Django REST Framework bulk CRUD view mixins

437 Stars 99 Forks Last release: Not found Other 75 Commits 5 Releases

Available items

No Items, yet!

The developer of this repository has not created any items for sale yet. Need a bug fixed? Help with integration? A different license? Create a request here:

Django REST Framework Bulk

.. image:: :target:

.. image:: :target:

Django REST Framework bulk CRUD view mixins.


Django REST Framework comes with many generic views however none of them allow to do bulk operations such as create, update and delete. To keep the core of Django REST Framework simple, its maintainer suggested to create a separate project to allow for bulk operations within the framework. That is the purpose of this project.


  • Python>=2.7
  • Django>=1.3
  • Django REST Framework >= 3.0.0
  • REST Framework >= 2.2.5 (only with Django<1.8 since DRF<3 does not support Django1.8)


Using pip::

$ pip install djangorestframework-bulk

or from source code::

$ pip install -e git+


The bulk views (and mixins) are very similar to Django REST Framework's own generic views (and mixins)::

from rest_framework_bulk import (

class FooSerializer(BulkSerializerMixin, ModelSerializer): class Meta(object): model = FooModel # only necessary in DRF3 list_serializer_class = BulkListSerializer

class FooView(ListBulkCreateUpdateDestroyAPIView): queryset = FooModel.objects.all() serializer_class = FooSerializer

The above will allow to create the following queries


# list queryset


# create single resource


# create multiple resources


# update multiple resources (requires all fields)


# partial update multiple resources


# delete queryset (see notes)


The bulk router can automatically map the bulk actions::

from rest_framework_bulk.routes import BulkRouter

class UserViewSet(BulkModelViewSet): model = User

def allow_bulk_destroy(self, qs, filtered):
    """Don't forget to fine-grain this method"""

router = BulkRouter() router.register(r'users', UserViewSet)


Django REST Framework made many API changes which included major changes in serializers. As a result, please note the following in order to use DRF-bulk with DRF3:

  • You must specify custom
    if your view(set) will require update functionality (when using
  • DRF3 removes read-only fields from

    . As a result, it is impossible to correlate each
    with a model instance to update since
    will be missing the model primary key since that is a read-only field. To deal with that, you must use
    mixin in your serializer class which will add the model primary key field back to the
    . By default
    field is used however you can customize that field by using
    in the serializers

    class FooSerializer(BulkSerializerMixin, ModelSerializer): class Meta(object): model = FooModel listserializerclass = BulkListSerializer updatelookupfield = 'slug'


Most API urls have two URL levels for each resource:

  1. url(r'foo/', ...)
  2. url(r'foo/(?P\d+)/', ...)

The second url however is not applicable for bulk operations because the url directly maps to a single resource. Therefore all bulk generic views only apply to the first url.

There are multiple generic view classes in case only a certail bulk functionality is required. For example

will only do bulk operations for creating resources. For a complete list of available generic view classes, please take a look at the source code at
as it is mostly self-explanatory.

Most bulk operations are pretty safe in terms of how they operate, that is you explicitly describe all requests. For example, if you need to update 3 specific resources, you have to explicitly identify those resources in the request's

data. The only exception to this is bulk delete. Consider a
request to the first url. That can potentially delete all resources without any special confirmation. To try to account for this, bulk delete mixin allows to implement a hook to determine if the bulk delete request should be allowed::
class FooView(BulkDestroyAPIView):
    def allow_bulk_destroy(self, qs, filtered):
        # custom logic here

    # default checks if the qs was filtered
    # qs comes from self.get_queryset()
    # filtered comes from self.filter_queryset(qs)
    return qs is not filtered

By default it checks if the queryset was filtered and if not will not allow the bulk delete to complete. The logic here is that if the request is filtered to only get certain resources, more attention was payed hence the action is less likely to be accidental. On how to filter requests, please refer to Django REST

_. Either way, please use bulk deletes with extreme caution since they can be dangerous.

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.