Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DRF 3 issue resolved #72

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 32 additions & 75 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,30 +36,58 @@ Using pip::

or from source code::

$ pip install -e git+http://github.com/miki725/django-rest-framework-bulk#egg=djangorestframework-bulk
$ pip install -e git+http://github.com/rajanmandanka/django-rest-framework-bulk#egg=djangorestframework-bulk

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not something that should be merged into this repo.


Example
-------

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

from rest_framework_bulk import (

from rest_framework_bulk.drf3.serializers import (
BulkListSerializer,
BulkSerializerMixin,
ListBulkCreateUpdateDestroyAPIView,
)

from rest_framework_bulk.generics import ListBulkCreateUpdateDestroyAPIView

serializer.py
-------------

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

class FooView(ListBulkCreateUpdateDestroyAPIView):
views.py
--------

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


urls.py
-------

from rest_framework import routers
from .views import UserViewSet , FooViewSet
from rest_framework_bulk.routes import BulkRouter


router = routers.DefaultRouter()
router.register(r'users', UserViewSet)

router2 = BulkRouter()
router2.register(r'foo', FooViewSet)

urlpatterns = []
urlpatterns += router.urls
urlpatterns += router2.urls

The above will allow to create the following queries

::
Expand Down Expand Up @@ -111,74 +139,3 @@ The bulk router can automatically map the bulk actions::

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

DRF3
----

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 ``list_serializer_class`` if your view(set)
will require update functionality (when using ``BulkUpdateModelMixin``)
* DRF3 removes read-only fields from ``serializer.validated_data``.
As a result, it is impossible to correlate each ``validated_data``
in ``ListSerializer`` with a model instance to update since ``validated_data``
will be missing the model primary key since that is a read-only field.
To deal with that, you must use ``BulkSerializerMixin`` mixin in your serializer
class which will add the model primary key field back to the ``validated_data``.
By default ``id`` field is used however you can customize that field
by using ``update_lookup_field`` in the serializers ``Meta``::

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

Notes
-----

Most API urls have two URL levels for each resource:

1. ``url(r'foo/', ...)``
2. ``url(r'foo/(?P<pk>\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 ``ListBulkCreateAPIView``
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 ``generics.py`` 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 ``PUT`` or ``PATCH`` data.
The only exception to this is bulk delete. Consider a ``DELETE``
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
`docs <http://www.django-rest-framework.org/api-guide/filtering>`_.
Either way, please use bulk deletes with extreme caution since they
can be dangerous.
20 changes: 10 additions & 10 deletions rest_framework_bulk/generics.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import unicode_literals, print_function
from rest_framework import mixins
from rest_framework.generics import GenericAPIView
from rest_framework.viewsets import ModelViewSet
from rest_framework.viewsets import ModelViewSet , GenericViewSet

from . import mixins as bulk_mixins

Expand All @@ -26,13 +26,13 @@
# ################################################## #

class BulkCreateAPIView(bulk_mixins.BulkCreateModelMixin,
GenericAPIView):
GenericViewSet):
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)


class BulkUpdateAPIView(bulk_mixins.BulkUpdateModelMixin,
GenericAPIView):
GenericViewSet):
def put(self, request, *args, **kwargs):
return self.bulk_update(request, *args, **kwargs)

Expand All @@ -41,14 +41,14 @@ def patch(self, request, *args, **kwargs):


class BulkDestroyAPIView(bulk_mixins.BulkDestroyModelMixin,
GenericAPIView):
GenericViewSet):
def delete(self, request, *args, **kwargs):
return self.bulk_destroy(request, *args, **kwargs)


class ListBulkCreateAPIView(mixins.ListModelMixin,
bulk_mixins.BulkCreateModelMixin,
GenericAPIView):
GenericViewSet):
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)

Expand All @@ -59,7 +59,7 @@ def post(self, request, *args, **kwargs):
class ListCreateBulkUpdateAPIView(mixins.ListModelMixin,
mixins.CreateModelMixin,
bulk_mixins.BulkUpdateModelMixin,
GenericAPIView):
GenericViewSet):
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)

Expand All @@ -77,7 +77,7 @@ class ListCreateBulkUpdateDestroyAPIView(mixins.ListModelMixin,
mixins.CreateModelMixin,
bulk_mixins.BulkUpdateModelMixin,
bulk_mixins.BulkDestroyModelMixin,
GenericAPIView):
GenericViewSet):
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)

Expand All @@ -97,7 +97,7 @@ def delete(self, request, *args, **kwargs):
class ListBulkCreateUpdateAPIView(mixins.ListModelMixin,
bulk_mixins.BulkCreateModelMixin,
bulk_mixins.BulkUpdateModelMixin,
GenericAPIView):
GenericViewSet):
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)

Expand All @@ -114,7 +114,7 @@ def patch(self, request, *args, **kwargs):
class ListBulkCreateDestroyAPIView(mixins.ListModelMixin,
bulk_mixins.BulkCreateModelMixin,
bulk_mixins.BulkDestroyModelMixin,
GenericAPIView):
GenericViewSet):
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)

Expand All @@ -129,7 +129,7 @@ class ListBulkCreateUpdateDestroyAPIView(mixins.ListModelMixin,
bulk_mixins.BulkCreateModelMixin,
bulk_mixins.BulkUpdateModelMixin,
bulk_mixins.BulkDestroyModelMixin,
GenericAPIView):
GenericViewSet):
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)

Expand Down