Django REST framework после массива объектов

Я использую Django REST framework для API и Angular SPA с Restangular для связи с API. Иногда мне нужно добавить более одного объекта с помощью API, и я думаю, что могу отправить их вместе в массив и сделать это в одном запросе.

Я получаю сообщение об ошибке wrong input, когда пытаюсь добавить более одного объекта из веб-интерфейса REST framework. Я передаю объекты или массив объектов, как показано ниже:

// this { "text": "gdhg", },{ "text": "gdhg", },{ "text": "gdhg", }
// or this [{ "text": "gdhg", },{ "text": "gdhg", },{ "text": "gdhg", }]

Но я получаю ParseError. Где я ошибаюсь и что мне нужно изменить, чтобы исправить это?


person valkirilov    schedule 05.04.2014    source источник


Ответы (5)


Другой пример, который поддерживает отправку массива, а также отправку одного объекта. Может быть полезно для тех, кто ищет такой пример.

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)
person Timothy Mugayi    schedule 11.10.2015

Я не уверен, что проблема все еще существует. Но решение, предложенное Fiver, у меня не сработало. Что работает для меня, так это переопределение ТОЛЬКО метода get_serializer.

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)

Если вы заметили, я устанавливаю по умолчанию many=True в аргументах get_serializer. Кроме этого ничего не требуется. Переопределение метода create также не требуется.

Также, если вы определяете методы pre_save и post_save в представлениях, ожидает список (итерируемый) в качестве аргумента (поскольку вы публикуете список) метода, а не только один объект.

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)
person vibhor    schedule 18.06.2014

Вот пример настройки массовой отправки сообщений в ListCreateAPIView с использованием 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)

Важной частью здесь является аргумент many=True метода get_serializer(). Затем, чтобы заставить Angular работать с этим, вы можете определить фабрику сервисов как:

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

Где важной частью является isArray: true. Если вы хотите сохранить публикацию отдельных объектов JSON, вы можете изменить save выше на что-то вроде saveBulk или подобное.

person Fiver    schedule 05.04.2014
comment
Я сделал это с ModelViewSet, и теперь получающий объект «список» не имеет атрибута «получить», потому что request.DATA — это список. Как решить эту проблему. Я тестирую его из веб-интерфейса API с отправкой json-массива объектов: [{ text: gdhg, }, { text: gdhg, }, { text: gdhg, }] - person valkirilov; 08.04.2014

Опираясь на ответ vibhor:

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)

Сделайте свое представление унаследованным от этого класса миксина, чтобы автоматически определять, следует ли использовать сериализатор many=True.

person dbkaplun    schedule 05.11.2015

Если вы хотите опубликовать список, вы должны передать JSON закодированные данные.

headers = {"Token": "35754sr7cvd7ryh454"}

recipients = [{'name': 'Ut est sed sed ipsa', 
               'email': '[email protected]', 
               'group': 'signers'},
              {'name': 'Development Ltda.', 
               'email': '[email protected]',
               'group': 'signers'}
             ]

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

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

Используйте аргумент ключевого слова json (не data), чтобы данные были закодированы в JSON, а Content-Type header установлено в application/json.

По умолчанию Django Rest Framework предполагает, что вы передаете ему один объект. Чтобы сериализовать набор запросов или список объектов вместо одного экземпляра объекта, вы должны передать флаг many=True при создании экземпляра сериализатора. Затем вы можете передать набор запросов или список объектов для сериализации.

Для этого вам придется переопределить метод .create() вашего представления:

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)

Документация: https://www.django-rest-framework.org/api-guide/serializers/#dealing-with-multiple-objects

person Iasmini Gomes    schedule 18.07.2020