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

Cha*_*haz 58 django django-rest-framework

我想使用Django Rest Framework和一个API调用来保存和更新多个实例.例如,假设我有一个可以拥有多个"教师"的"课堂"模型.如果我想创建多个教师,然后更新他们所有的课堂号码,我该怎么做?我是否必须为每位老师拨打API电话?

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

Tom*_*eld 67

我知道这是前一段时间被问到的,但我发现它同时试图弄清楚这一点.

事实证明,如果many=True在为模型实例化序列化程序类时传递,则它可以接受多个对象.

这是提到这里的Django的REST框架文件

对于我的情况,我的观点看起来像这样:

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

我真的不想写一大堆样板文件只是为了直接控制序列化程序的实例化并传递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', )
Run Code Online (Sandbox Code Playgroud)

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

[
    {'loads':'foo','of':'bar','fields':'buzz'},
    {'loads':'fizz','of':'bazz','fields':'errrrm'}
]
Run Code Online (Sandbox Code Playgroud)

使用这些详细信息创建了两个资源.哪个好看.

  • 对我来说不起作用{"non_field_errors":["无效数据.预计字典,但得到列表." ]} (10认同)

Rog*_*ins 39

我得出了与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)
Run Code Online (Sandbox Code Playgroud)

  • 这件事情让我感到很快乐!我确认这工作正常并接受列表和字典。 (3认同)
  • @RogerCollins如果列表项之一引发验证错误,则整个请求将失败。有没有办法跳过无效项目并创建其余实例? (2认同)

小智 16

这是另一种解决方案,您不需要覆盖序列化__init__方法.只需覆盖您的视图(ModelViewSet)'create'方法.注意事项many=isinstance(request.data,list).这里many=True当您发送要创建的对象数组时,以及False只发送一个对象时.这样,您可以保存项目和列表!

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)
Run Code Online (Sandbox Code Playgroud)

  • 这个答案似乎更直接,以及我希望如何实现此功能。 (4认同)
  • 这个有效,而投票最高的答案对我不起作用。 (2认同)

aka*_*nom 11

我无法弄清楚将request.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)
Run Code Online (Sandbox Code Playgroud)

然后我在客户端运行相当于这个:

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

  • 我认为关键是在`get_serializer`调用中包含`many = True` (2认同)

Dan*_*ral 6

我认为尊重该框架的最佳架构的最佳方法是创建一个如下所示的mixin:

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)
Run Code Online (Sandbox Code Playgroud)

然后,您可以像这样覆盖ModelViewSet的CreateModelMixin:

class <MyModel>ViewSet(CreateListModelMixin, viewsets.ModelViewSet):
    ...
    ...
Run Code Online (Sandbox Code Playgroud)

现在在客户端中,您可以像这样工作:

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

要么

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

thingClientResource.post(thing)
Run Code Online (Sandbox Code Playgroud)

编辑:

正如罗杰·柯林斯认为她的反应是更聪明的覆盖比“创造”的get_serializer方法。


Teh*_*ila 6

你可以简单地覆盖get_serializer法在APIView并传递many=Trueget_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)
Run Code Online (Sandbox Code Playgroud)


Huz*_*yed 5

我想出了一个简单的例子 post

序列化器.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',
        ]  
Run Code Online (Sandbox Code Playgroud)

视图.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)
Run Code Online (Sandbox Code Playgroud)

这些行是多实例的实际逻辑 -

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)
Run Code Online (Sandbox Code Playgroud)

如果你对 many=True 感到困惑,请看这个

当我们发送数据时,它会在里面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"
    }
]
Run Code Online (Sandbox Code Playgroud)