Подтвердить что ты не робот

Как создать несколько экземпляров модели с помощью Django Rest Framework?

Я хотел бы сохранить и обновить несколько экземпляров с помощью Django Rest Framework с помощью одного вызова API. Например, допустим, у меня есть модель "Класс", которая может иметь несколько "Учителей". Если бы я хотел создать несколько учителей и позже обновить все номера своих классов, как бы я это сделал? Должен ли я выполнять вызов API для каждого преподавателя?

Я знаю, что в настоящее время мы не можем сохранить вложенные модели, но я хотел бы знать, можем ли мы сохранить его на уровне учителя. Спасибо!

4b9b3361

Ответ 1

Я знаю, что это было задано некоторое время назад, но я нашел его, пытаясь понять это сам.

Оказывается, если вы передаете many=True при создании экземпляра класса serializer для модели, он может затем принять несколько объектов.

Это упоминается здесь в django rest framework docs

В моем случае мое мнение выглядело так:

class ThingViewSet(viewsets.ModelViewSet):
    """This view provides list, detail, create, retrieve, update
    and destroy actions for Things."""
    model = Thing
    serializer_class = ThingSerializer

Я действительно не хотел писать написание шаблона только для непосредственного управления созданием сериализатора и передачи many=True, поэтому в моем классе сериализатора вместо этого вместо __init__ я заменяю:

class ThingSerializer(serializers.ModelSerializer):
    def __init__(self, *args, **kwargs):
        many = kwargs.pop('many', True)
        super(ThingSerializer, self).__init__(many=many, *args, **kwargs)

    class Meta:
        model = Thing
        fields = ('loads', 'of', 'fields', )

Проводка данных в список URL для этого представления в формате:

[
    {'loads':'foo','of':'bar','fields':'buzz'},
    {'loads':'fizz','of':'bazz','fields':'errrrm'}
]

Создал два ресурса с этими деталями. Что было приятно.

Ответ 2

Я пришел к аналогичному выводу, как Даниэль Альбаррал, но здесь более сжатое решение:

class CreateListModelMixin(object):

    def get_serializer(self, *args, **kwargs):
        """ if an array is passed, set serializer to many """
        if isinstance(kwargs.get('data', {}), list):
            kwargs['many'] = True
        return super(CreateListModelMixin, self).get_serializer(*args, **kwargs)

Ответ 3

Я не мог понять, как получить запрос .DATA для преобразования из словаря в массив, что ограничивало мою способность к решению Tom Manterfield. Вот мое решение:

class ThingSerializer(serializers.ModelSerializer):
    def __init__(self, *args, **kwargs):
        many = kwargs.pop('many', True)
        super(ThingSerializer, self).__init__(many=many, *args, **kwargs)

    class Meta:
        model = Thing
        fields = ('loads', 'of', 'fields', )

class ThingViewSet(mixins.CreateModelMixin, viewsets.GenericViewSet ):
    queryset = myModels\
        .Thing\
        .objects\
        .all()
    serializer_class = ThingSerializer

    def create(self, request, *args, **kwargs):
        self.user = request.user
        listOfThings = request.DATA['things']

        serializer = self.get_serializer(data=listOfThings, files=request.FILES, 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)

И затем я запускаю эквивалент этого на клиенте:

var things = {    
    "things":[
        {'loads':'foo','of':'bar','fields':'buzz'},
        {'loads':'fizz','of':'bazz','fields':'errrrm'}]
}
thingClientResource.post(things)

Ответ 4

Я думаю, что лучше всего уважать пропущенную архитектуру фреймворка - создать такой микс:

class CreateListModelMixin(object):

    def create(self, request, *args, **kwargs):
        """
            Create a list of model instances if a list is provides or a
            single model instance otherwise.
        """
        data = request.data
        if isinstance(data, list):
            serializer = self.get_serializer(data=request.data, many=True)
        else:
            serializer = self.get_serializer(data=request.data)
        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)

Затем вы можете переопределить CreateModelMixin ModelViewSet следующим образом:

class <MyModel>ViewSet(CreateListModelMixin, viewsets.ModelViewSet):
    ...
    ...

Теперь в клиенте вы можете работать следующим образом:

var things = [    
    {'loads':'foo','of':'bar','fields':'buzz'},
    {'loads':'fizz','of':'bazz','fields':'errrrm'}
]
thingClientResource.post(things)

или

var thing = {
    'loads':'foo','of':'bar','fields':'buzz'
}

thingClientResource.post(thing)

EDIT:

Как говорит Роджер Коллинз в ее ответ, более умный, чтобы перезаписать метод get_serializer, чем 'create'.

Ответ 5

Страница Общие виды в Документация Django REST Framework что ListCreateAPIView общий вид "используется для конечных точек чтения и записи для представления коллекции экземпляров модели".

Что бы я начал искать (и я собираюсь на самом деле, так как нам тоже понадобится эта функциональность в нашем проекте).

Обратите внимание также, что examples на странице Generic Views использует ListCreateAPIView.

Ответ 6

Здесь другое решение, вам не нужно переопределять ваш метод сериализации __init__. Просто переопределите метод просмотра (ModelViewSet) 'create'. Обратите внимание на many=isinstance(request.data,list). Здесь many=True, когда вы отправляете массив объектов для создания, и False при отправке только одного. Таким образом, вы можете сохранить как элемент, так и список!

from rest_framework import status
from rest_framework.response import Response

class ThingViewSet(viewsets.ModelViewSet):

"""This view snippet provides both list and item create functionality."""

    #I took the liberty to change the model to queryset
    queryset = Thing.objects.all()
    serializer_class = ThingSerializer

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data, many=isinstance(request.data,list))
        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)

Ответ 7

Вы можете просто перезаписать метод get_serializer в вашем APIView и передать many=True в get_serializer базового вида следующим образом:

class SomeAPIView(CreateAPIView):
    queryset = SomeModel.objects.all()
    serializer_class = SomeSerializer

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