简体   繁体   English

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

[英]How do I create multiple model instances with Django Rest Framework?

I would like to save and update multiple instances using the Django Rest Framework with one API call.我想通过一个 API 调用使用 Django Rest Framework 保存和更新多个实例。 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 调用?

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!谢谢!

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.事实证明,如果在实例化模型的序列化器类时传递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.这很好。

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)

Here's another solution, you don't need to override your serializers __init__ method.这是另一种解决方案,您不需要覆盖序列化程序的__init__方法。 Just override your view's (ModelViewSet) 'create' method.只需覆盖您的视图 (ModelViewSet) 的'create'方法。 Notice many=isinstance(request.data,list) .注意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.当您发送要创建的对象数组时,这里many=True当您只发送一个对象时, False This way, you can save both an item and a list!这样,您可以同时保存项目和列表!

from rest_framework import status, viewsets
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)

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.我无法弄清楚让 request.DATA 从字典转换为数组 - 这限制了我对 Tom Manterfield 的解决方案的工作能力。 Here is my solution:这是我的解决方案:

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)

I think the best approach to respect the proposed architecture of the framework will be to create a mixin like this:我认为尊重框架建议架构的最佳方法是创建一个像这样的 mixin:

class CreateListModelMixin(object):

    def create(self, request, *args, **kwargs):
        """
            Create a list of model instances if a list is provided 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 suggests in his response is more clever to overwrite the get_serializer method than the 'create'.正如罗杰柯林斯在他的回应中所暗示的那样,覆盖 get_serializer 方法比“创建”方法更聪明。

You can simply overwrite the get_serializer method in your APIView and pass many=True into get_serializer of the base view like so:您可以简单地覆盖get_serializer中的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)

I came up with simple example in post我在post提出了一个简单的例子

Serializers.py序列化器.py

from rest_framework import serializers
from movie.models import Movie

class MovieSerializer(serializers.ModelSerializer):

    class Meta:
        model = Movie
        fields = [
            'popularity',
            'director',
            'genre',
            'imdb_score',
            'name',
        ]  

Views.py视图.py

from rest_framework.response import Response
from rest_framework import generics
from .serializers import MovieSerializer
from movie.models import Movie
from rest_framework import status
from rest_framework.permissions import IsAuthenticated

class MovieList(generics.ListCreateAPIView):
    queryset = Movie.objects.all().order_by('-id')[:10]
    serializer_class = MovieSerializer
    permission_classes = (IsAuthenticated,)

    def list(self, request):
        queryset = self.get_queryset()
        serializer = MovieSerializer(queryset, many=True)
        return Response(serializer.data)

    def post(self, request, format=None):
        data = request.data
        if isinstance(data, list):  # <- is the main logic
            serializer = self.get_serializer(data=request.data, many=True)
        else:
            serializer = self.get_serializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

These line are the actual logic of Multiple Instance -这些行是多实例的实际逻辑 -

data = request.data
if isinstance(data, list):  # <- is the main logic
      serializer = self.get_serializer(data=request.data, many=True)
else:
      serializer = self.get_serializer(data=request.data)

If you are confused with many=True, see this 如果你对 many=True 感到困惑,请看这个

When we send data it will be inside list somewhat like this -当我们发送数据时,它将在list有点像这样 -

[
    {
        "popularity": 84.0,
        "director": "Stanley Kubrick",
        "genre": [
            1,
            6,
            10
        ],
        "imdb_score": 8.4,
        "name": "2001 : A Space Odyssey"
    },
    {
        "popularity": 84.0,
        "director": "Stanley Kubrick",
        "genre": [
            1,
            6,
            10
        ],
        "imdb_score": 8.4,
        "name": "2001 : A Space Odyssey"
    }
]

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 Framework 文档中通用视图页面指​​出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

Most straightforward method I've come across:我遇到的最直接的方法:

    def post(self, request, *args, **kwargs):
        serializer = ThatSerializer(data=request.data, many=isinstance(request.data, list))
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

[ { "id": 1123, "title": "Learning JSON", "author": "Book by David Ascher and Mark LUTZ" }, { "id": 1132, "title": "Learning JSON", "author ": "David Ascher 和 Mark LUTZ 的书"}]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM