Django REST framework post array of objects

后端 未结 5 626
挽巷
挽巷 2020-12-16 14:43

I am using Django REST framework for API and Angular SPA with Restangular to communicate with the API. Sometimes, I have to add more than one object using the API and I thin

相关标签:
5条回答
  • 2020-12-16 15:04

    Here's an example for setting up bulk POSTing in a ListCreateAPIView using the Django REST Framework:

    class SomethingList(generics.ListCreateAPIView):
        model = Something
        serializer_class = SomethingSerializer
    
        def create(self, request, *args, **kwargs):
            serializer = self.get_serializer(data=request.DATA, many=True)
            if serializer.is_valid():
                serializer.save()
                headers = self.get_success_headers(serializer.data)
                return Response(serializer.data, status=status.HTTP_201_CREATED,
                                headers=headers)
    
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    

    The important part here is the many=True argument to the get_serializer() method. Then, to make Angular play nice with this, you can define a service factory as:

    .factory('Something', ['$resource', function ($resource) {
        return $resource(
            "url_to_something",
            {},
            {
                save: {
                    method: 'POST',
                    isArray: true
                }
            }
        );
    }])
    

    Where the important part is the isArray: true. If you want to preserve posting single JSON objects, you could change save above to something like saveBulk or similar.

    0 讨论(0)
  • 2020-12-16 15:12

    Another example that supports posting an array as well as posting a single object. Might be useful for anyone else looking for such an example.

    class BookViewSet(mixins.CreateModelMixin, mixins.ListModelMixin, viewsets.GenericViewSet):
        """
        ViewSet create and list books
    
        Usage single : POST
        {
            "name":"Killing Floor: A Jack Reacher Novel", 
            "author":"Lee Child"
        }
    
        Usage array : POST
        [{  
            "name":"Mr. Mercedes: A Novel (The Bill Hodges Trilogy)",
            "author":"Stephen King"
        },{
            "name":"Killing Floor: A Jack Reacher Novel", 
            "author":"Lee Child"
        }]
        """
        queryset = Book.objects.all()
        serializer_class = BookSerializer
        search_fields = ('name','author')
    
        def create(self, request, *args, **kwargs):
            """
            #checks if post request data is an array initializes serializer with many=True
            else executes default CreateModelMixin.create function 
            """
            is_many = isinstance(request.data, list)
            if not is_many:
                return super(BookViewSet, self).create(request, *args, **kwargs)
            else:
                serializer = self.get_serializer(data=request.data, many=True)
                serializer.is_valid(raise_exception=True)
                self.perform_create(serializer)
                headers = self.get_success_headers(serializer.data)
                return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
    
    0 讨论(0)
  • 2020-12-16 15:21

    Building on vibhor's answer:

    class ListableViewMixin(object):
        def get_serializer(self, instance=None, data=None, many=False, *args, **kwargs):
            return super(ListableViewMixin, self).get_serializer(
                instance=instance, data=data, many=isinstance(instance, list) or isinstance(data, list),
                *args, **kwargs)
    

    Make your view inherit from this mixin class to automatically determine if a many=True serializer should be used.

    0 讨论(0)
  • 2020-12-16 15:26

    I am not sure if the problem still exist. But the solution suggested by fiver did not work for me. What works for me is overriding the get_serializer method ONLY.

    def get_serializer(self, instance=None, data=None,
                        files=None, many=True, partial=False):
        return super(ViewName, self).get_serializer(instance, data, files,
                                                        many, partial)
    

    If you will notice I am setting default many=True in arguments of get_serializer. Apart from that nothing is required. Overridng of create method is also not required.

    Also if you are defining the pre_save and post_save method in the views, expects the list(iterable) as the argument(as you are posting the list) of method not just a single object.

    def post_save(self, objects, *args, **kwargs):
        """
        In the post_save, list of obj has been created
        """
        for obj in objects:
            do_something_with(obj)
    
    0 讨论(0)
  • 2020-12-16 15:28

    If you want to post a list you have to pass in JSON encoded data.

    headers = {"Token": "35754sr7cvd7ryh454"}
    
    recipients = [{'name': 'Ut est sed sed ipsa', 
                   'email': 'dogoka@mailinator.com', 
                   'group': 'signers'},
                  {'name': 'Development Ltda.', 
                   'email': 'test@test.com',
                   'group': 'signers'}
                 ]
    
    requests.post(url, json=recipients, headers=headers)
    

    requests.post(url, json=recipients, headers=headers)

    Use json keyword argument (not data) so the data is encoded to JSON and the Content-Type header is set to application/json.

    By default, Django Rest Framework assumes you are passing it a single object. To serialize a queryset or list of objects instead of a single object instance, you should pass the many=True flag when instantiating the serializer. You can then pass a queryset or list of objects to be serialized.

    To do it, you'll have to override the .create() method of your view:

    def create(self, request, *args, **kwargs):
        many = True if isinstance(request.data, list) else False
    
        serializer = self.get_serializer(data=request.data, many=many)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
    

    Documentation: https://www.django-rest-framework.org/api-guide/serializers/#dealing-with-multiple-objects

    0 讨论(0)
提交回复
热议问题