如何使用Django Rest框架创建多个模型实例?

时间:2021-11-17 02:53:15

I would like to save and update multiple instances using the Django Rest Framework with one API call. For example, let's say I have a "Classroom" model that can have multiple "Teachers". If I wanted to create multiple teachers and later update all of their classroom numbers how would I do that? Do I have to make an API call for each teacher?

我希望通过一个API调用来使用Django Rest框架来保存和更新多个实例。例如,假设我有一个“教室”模型,它可以有多个“老师”。如果我想创建多个老师,然后更新所有的教室号码我该怎么做呢?我是否需要为每个老师做API调用?

I know currently we can't save nested models, but I would like to know if we can save it at the teacher level. Thanks!

我知道目前我们不能保存嵌套模型,但是我想知道我们是否可以在教师级别保存它。谢谢!

7 个解决方案

#1


52  

I know this was asked a while ago now but I found it whilst trying to figure this out myself.

我知道刚才有人问过这个问题,但我是在自己试图弄清楚的时候发现的。

It turns out if you pass many=True when instantiating the serializer class for a model, it can then accept multiple objects.

如果在为模型实例化serializer类时传递many=True,那么它就可以接受多个对象。

This is mentioned here in the django rest framework docs

在django rest框架文档中提到了这一点

For my case, my view looked like this:

对我来说,我的观点是这样的:

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

I didn't really want to go writing a load of boilerplate just to have direct control over the instantiation of the serializer and pass many=True, so in my serializer class I override the __init__ instead:

我并不想写一堆样板文件只是为了直接控制序列化器的实例化并传递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', )

Posting data to the list URL for this view in the format:

将数据以以下格式发布到此视图的列表URL:

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

Created two resources with those details. Which was nice.

使用这些细节创建了两个资源。这是很好的。

#2


22  

I came to a similar conclusion as Daniel Albarral, but here's a more succinct solution:

我和丹尼尔·阿尔巴拉尔(Daniel Albarral)得出了类似的结论,但这里有一个更简洁的解决方案:

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


11  

I couldn't quite figure out getting the request.DATA to convert from a dictionary to an array - which was a limit on my ability to Tom Manterfield's solution to work. Here is my solution:

我不太明白怎么能得到这个请求。从字典转换到数组的数据——这是我对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)

And then I run the equivalent of this on the client:

然后我在客户身上做了同样的事情:

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

#4


5  

You can simply overwrite the get_serializer method in your APIView and pass many=True into get_serializer of the base view like so:

您可以在您的APIView中覆盖get_serializer方法,并将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)

#5


5  

Here's another solution, you don't need to override your serializers __init__ method. Just override your view's (ModelViewSet) 'create' method. Notice many=isinstance(request.data,list). Here many=True when you send an array of objects to create, and False when you send just the one. This way, you can save both an item and a list!

这是另一个解决方案,您不需要重写您的序列化器__init__方法。只需重写视图的(ModelViewSet)“创建”方法。注意到很多= isinstance(request.data列表)。这里的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)

#6


4  

I think the best aprouch to respect the propoused architecture of the framework will be to create a mixin like this:

我认为,要尊重框架的架构,最好的办法就是创建这样的一个混合体:

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)

Then you can override the CreateModelMixin of ModelViewSet like this:

然后可以重写ModelViewSet的CreateModelMixin,如下所示:

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

Now in the client you can work like this:

在客户端,你可以这样工作:

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

or

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

thingClientResource.post(thing)

EDIT:

编辑:

As Roger Collins suggest in her response is more clever to overwrite the get_serializer method than the 'create'.

正如Roger Collins在她的回复中所建议的,重写get_serializer方法比“create”更聪明。

#7


3  

The Generic Views page in Django REST Framework's documentation states that the ListCreateAPIView generic view is "used for read-write endpoints to represent a collection of model instances".

Django REST框架文档中的通用视图页面声明,ListCreateAPIView通用视图“用于读写端点,以表示模型实例的集合”。

That's where I would start looking (and I'm going to actually, since we'll need this functionality in our project soon as well).

这就是我要开始寻找的地方(实际上,我也将开始寻找,因为我们的项目很快也需要这个功能)。

Note also that the examples on the Generic Views page happen to use ListCreateAPIView.

还要注意,泛型视图页面上的示例碰巧使用了ListCreateAPIView。

#1


52  

I know this was asked a while ago now but I found it whilst trying to figure this out myself.

我知道刚才有人问过这个问题,但我是在自己试图弄清楚的时候发现的。

It turns out if you pass many=True when instantiating the serializer class for a model, it can then accept multiple objects.

如果在为模型实例化serializer类时传递many=True,那么它就可以接受多个对象。

This is mentioned here in the django rest framework docs

在django rest框架文档中提到了这一点

For my case, my view looked like this:

对我来说,我的观点是这样的:

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

I didn't really want to go writing a load of boilerplate just to have direct control over the instantiation of the serializer and pass many=True, so in my serializer class I override the __init__ instead:

我并不想写一堆样板文件只是为了直接控制序列化器的实例化并传递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', )

Posting data to the list URL for this view in the format:

将数据以以下格式发布到此视图的列表URL:

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

Created two resources with those details. Which was nice.

使用这些细节创建了两个资源。这是很好的。

#2


22  

I came to a similar conclusion as Daniel Albarral, but here's a more succinct solution:

我和丹尼尔·阿尔巴拉尔(Daniel Albarral)得出了类似的结论,但这里有一个更简洁的解决方案:

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


11  

I couldn't quite figure out getting the request.DATA to convert from a dictionary to an array - which was a limit on my ability to Tom Manterfield's solution to work. Here is my solution:

我不太明白怎么能得到这个请求。从字典转换到数组的数据——这是我对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)

And then I run the equivalent of this on the client:

然后我在客户身上做了同样的事情:

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

#4


5  

You can simply overwrite the get_serializer method in your APIView and pass many=True into get_serializer of the base view like so:

您可以在您的APIView中覆盖get_serializer方法,并将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)

#5


5  

Here's another solution, you don't need to override your serializers __init__ method. Just override your view's (ModelViewSet) 'create' method. Notice many=isinstance(request.data,list). Here many=True when you send an array of objects to create, and False when you send just the one. This way, you can save both an item and a list!

这是另一个解决方案,您不需要重写您的序列化器__init__方法。只需重写视图的(ModelViewSet)“创建”方法。注意到很多= isinstance(request.data列表)。这里的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)

#6


4  

I think the best aprouch to respect the propoused architecture of the framework will be to create a mixin like this:

我认为,要尊重框架的架构,最好的办法就是创建这样的一个混合体:

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)

Then you can override the CreateModelMixin of ModelViewSet like this:

然后可以重写ModelViewSet的CreateModelMixin,如下所示:

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

Now in the client you can work like this:

在客户端,你可以这样工作:

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

or

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

thingClientResource.post(thing)

EDIT:

编辑:

As Roger Collins suggest in her response is more clever to overwrite the get_serializer method than the 'create'.

正如Roger Collins在她的回复中所建议的,重写get_serializer方法比“create”更聪明。

#7


3  

The Generic Views page in Django REST Framework's documentation states that the ListCreateAPIView generic view is "used for read-write endpoints to represent a collection of model instances".

Django REST框架文档中的通用视图页面声明,ListCreateAPIView通用视图“用于读写端点,以表示模型实例的集合”。

That's where I would start looking (and I'm going to actually, since we'll need this functionality in our project soon as well).

这就是我要开始寻找的地方(实际上,我也将开始寻找,因为我们的项目很快也需要这个功能)。

Note also that the examples on the Generic Views page happen to use ListCreateAPIView.

还要注意,泛型视图页面上的示例碰巧使用了ListCreateAPIView。