diff --git a/rest_framework/generics.py b/rest_framework/generics.py index 9ae8cf0aa..4ccac60f4 100644 --- a/rest_framework/generics.py +++ b/rest_framework/generics.py @@ -6,6 +6,9 @@ from rest_framework import views, mixins from rest_framework.settings import api_settings from django.views.generic.detail import SingleObjectMixin from django.views.generic.list import MultipleObjectMixin +from rest_framework.mixins import ListModelMixin, CreateBulkMixin +from rest_framework.renderers import BrowsableAPIRenderer,\ + BulckBrowsableAPIRenderer ### Base classes for the generic views ### @@ -253,3 +256,28 @@ class RetrieveUpdateDestroyAPIView(mixins.RetrieveModelMixin, def delete(self, request, *args, **kwargs): return self.destroy(request, *args, **kwargs) + +class BulckAPIView(ListModelMixin, + CreateBulkMixin, + MultipleObjectAPIView): + """ + Concrete view for listing a queryset or creating a model instance. + """ + + + def __init__(self, **kwargs): + self.renderer_classes = self._renderer_classes_modefier() + MultipleObjectAPIView.__init__(self, **kwargs) + + def _renderer_classes_modefier(self): + renderer_classes = self.renderer_classes + if BrowsableAPIRenderer in renderer_classes: + index = renderer_classes.index(BrowsableAPIRenderer) + renderer_classes[index] = BulckBrowsableAPIRenderer + return renderer_classes + + def get(self, request, *args, **kwargs): + return self.list(request, *args, **kwargs) + + def post(self, request, *args, **kwargs): + return self.create(request, *args, **kwargs) diff --git a/rest_framework/mixins.py b/rest_framework/mixins.py index d898ca128..058db9648 100644 --- a/rest_framework/mixins.py +++ b/rest_framework/mixins.py @@ -140,3 +140,52 @@ class DestroyModelMixin(object): obj = self.get_object() obj.delete() return Response(status=status.HTTP_204_NO_CONTENT) + +class CreateBulkMixin(object): + """ + Create model instances in bulk. + Should be mixed in with any `BaseView`. and not with CreateModelMixin + """ + def create(self, request, *args, **kwargs): + data = request.DATA['list'] + valid_inputs = [] + valid_objects = [] + invalid_inputs = [] + errors = [] + for element in data: + serializer = self.get_serializer(data=element, files=request.FILES) # not sure about files + if serializer.is_valid(): + self.pre_save(serializer.object) + valid_objects.append(serializer.object) + valid_inputs.append(element) + else: + invalid_inputs.append(element) + errors.append(serializer.errors) + # remove duplicates + self.model.objects.bulk_create(list(set(valid_objects))) + return Response({ + 'Succeeded': valid_inputs, + 'failed': invalid_inputs, + 'errors': errors + }) + + def pre_save(self, obj): + # a stub method to be overriden to do any pre processing + pass + + +class UpdateBulkMixin(object): + """ + Update model instances in bulk. + Should be mixed in with `MultibleObjectBaseView`. and not with UpdateModelMixin + """ + def update(self, request, *args, **kwargs): + raise NotImplementedError + +class DestroyBulkMixin(object): + """ + Destroy model instances in bulk. + Should be mixed in with `MultibleObjectBaseView`. and not with DestroyModelMixin + """ + def destroy(self, request, *args, **kwargs): + raise NotImplementedError diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index a65254042..851f6e3ac 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -463,3 +463,10 @@ class BrowsableAPIRenderer(BaseRenderer): response.status_code = status.HTTP_200_OK return ret + +class BulckBrowsableAPIRenderer(BrowsableAPIRenderer): + def get_form(self, view, method, request): + # TODO: change this to render multible create form + if not method=='POST': + return BrowsableAPIRenderer.get_form(self, view, method, request) + return None \ No newline at end of file