한 번의 API 호출로 Django Rest Framework를 사용하여 여러 인스턴스를 저장하고 업데이트하고 싶습니다. 예를 들어 여러 "교사"를 가질 수있는 "교실"모델이 있다고 가정 해 보겠습니다. 여러 명의 선생님을 만들고 나중에 모든 교실 번호를 업데이트하려면 어떻게해야합니까? 각 교사에 대해 API 호출을해야합니까?
현재 중첩 된 모델을 저장할 수 없다는 것을 알고 있지만 교사 수준에서 저장할 수 있는지 알고 싶습니다. 감사!
한 번의 API 호출로 Django Rest Framework를 사용하여 여러 인스턴스를 저장하고 업데이트하고 싶습니다. 예를 들어 여러 "교사"를 가질 수있는 "교실"모델이 있다고 가정 해 보겠습니다. 여러 명의 선생님을 만들고 나중에 모든 교실 번호를 업데이트하려면 어떻게해야합니까? 각 교사에 대해 API 호출을해야합니까?
현재 중첩 된 모델을 저장할 수 없다는 것을 알고 있지만 교사 수준에서 저장할 수 있는지 알고 싶습니다. 감사!
답변:
나는 이것이 얼마 전에 요청되었다는 것을 알고 있지만 이것을 스스로 알아 내려고 노력하는 동안 발견했습니다.
many=True
모델에 대한 serializer 클래스를 인스턴스화 할 때 통과 하면 여러 개체를 받아 들일 수 있습니다.
이것은 django rest 프레임 워크 문서에 언급되어 있습니다 .
제 경우에는 다음과 같이 보입니다.
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'}
]
이러한 세부 정보로 두 개의 리소스를 만들었습니다. 좋았어.
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)
여기에 또 다른 해결책이 있습니다 __init__
. serializers 메서드 를 재정의 할 필요가 없습니다 . 뷰의 (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)
transaction.atomic()
있는지 모든 요소가 추가 될 수 있도록 블록
Expected a dictionary, but got list.
받아 들인 대답에 오류가 생겼고 이것은 나를 위해 수정했습니다. 감사.
딕셔너리에서 배열로 변환하기 위해 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)
many=True
에서 get_serializer
호출
프레임 워크의 제안 된 아키텍처를 존중하는 가장 좋은 방법은 다음과 같은 믹스 인을 만드는 것입니다.
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가 그녀의 응답 에서 제안했듯이 'create'보다 get_serializer 메서드를 덮어 쓰는 것이 더 영리합니다.
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)
data
키워드 인수 가 전달 .is_valid()
되면 직렬화 된 .data
표현 에 액세스하기 전에 호출해야합니다 . .is_valid()
먼저 전화를 걸 거나 .initial_data
대신 액세스 해야합니다 .
from rest_framework.fields import empty def get_serializer(self, instance=None, data=empty, many=False, partial=False): return super(SomeAPIView, self).get_serializer(instance=instance, data=data, many=True, partial=partial)
Django REST Framework의 문서 에 있는 Generic Views 페이지 에는 ListCreateAPIView 일반보기가 "모델 인스턴스 컬렉션을 나타내는 읽기-쓰기 끝점에 사용됨"이 명시되어 있습니다.
여기서부터 살펴볼 것입니다 (실제로 프로젝트에도이 기능이 필요할 것이기 때문입니다).
Generic Views 페이지 의 예제 는 ListCreateAPIView
.
나는 간단한 예를 생각해 냈다. 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)
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"
}
]
내가 본 가장 간단한 방법 :
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)