我想通过一次API调用使用Django Rest Framework保存和更新多个实例。例如,假设我有一个可以拥有多个“教师”的“课堂”模型。如果我想创建多个教师,然后更新他们所有的课堂号码,我该怎么做?我是否必须为每位教师进行API调用?
我知道目前我们无法保存嵌套模型,但我想知道我们是否可以在教师级别保存它。 谢谢!
答案 0 :(得分:60)
我知道这个问题刚才被问到,但是我在尝试自己解决这个问题时发现了它。
如果在为模型实例化序列化程序类时传递many=True
,则可以接受多个对象。
这在django rest framework docs中提到here
就我而言,我的观点看起来像这样:
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', )
以下列格式将数据发布到此视图的列表网址:
[
{'loads':'foo','of':'bar','fields':'buzz'},
{'loads':'fizz','of':'bazz','fields':'errrrm'}
]
使用这些详细信息创建了两个资源。哪个很好。
答案 1 :(得分:34)
我得出了与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)
答案 2 :(得分:12)
这是另一个解决方案,您不需要覆盖序列化程序__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)
答案 3 :(得分: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)
然后我在客户端运行相当于这个:
var things = {
"things":[
{'loads':'foo','of':'bar','fields':'buzz'},
{'loads':'fizz','of':'bazz','fields':'errrrm'}]
}
thingClientResource.post(things)
答案 4 :(得分:6)
您只需覆盖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)
我认为尊重该框架的建议架构的最佳方法是创建一个这样的混合:
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)
然后你可以像这样覆盖ModelViewSet的CreateModelMixin:
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)
编辑:
正如Roger Collins在her response中所建议的那样,覆盖get_serializer方法比“创建”更聪明。
答案 6 :(得分:3)
Generic Views中的Django REST Framework's documentation页面指出ListCreateAPIView通用视图“用于读写端点以表示模型实例的集合”。
这就是我要开始寻找的地方(我实际上是这样,因为我们很快就会在项目中使用这个功能)。
另请注意,“通用视图”页面上的examples恰好使用ListCreateAPIView
。
答案 7 :(得分:2)
我在post
中想到了一个简单的例子
Serializers.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
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)
这些行是多实例的实际逻辑-
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
当我们发送数据时,它会在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"
}
]
答案 8 :(得分:0)
我遇到的最简单的方法:
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)