Ich möchte mehrere Instanzen mit dem Django Rest Framework mit einem API-Aufruf speichern und aktualisieren. Angenommen, ich habe ein "Klassenzimmer" -Modell, das mehrere "Lehrer" haben kann. Wenn ich mehrere Lehrer erstellen und später alle Klassenzimmernummern aktualisieren wollte, wie würde ich das tun? Muss ich für jeden Lehrer einen API-Aufruf machen?
Ich weiß, dass wir derzeit keine verschachtelten Modelle speichern können, aber ich würde gerne wissen, ob wir es auf Lehrerlevel speichern können.
Ich weiß, das wurde vor einiger Zeit gefragt, aber ich habe es gefunden, als ich versuchte, das selbst herauszufinden.
Wenn Sie bei der Instanziierung der Serialisiererklasse für ein Modell many=True
übergeben, kann es mehrere Objekte akzeptieren.
Dies wird hier in den Django-Rest-Framework-Dokumenten erwähnt
Für meinen Fall sah meine Ansicht so aus:
class ThingViewSet(viewsets.ModelViewSet):
"""This view provides list, detail, create, retrieve, update
and destroy actions for Things."""
model = Thing
serializer_class = ThingSerializer
Ich wollte nicht wirklich eine Ladung Boilerplate schreiben, um die Instantiierung des Serialisierers direkt zu kontrollieren und many=True
zu übergeben. In meiner Serialisiererklasse überschreibe ich stattdessen den __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', )
Buchung von Daten an die Listen-URL für diese Ansicht im Format:
[
{'loads':'foo','of':'bar','fields':'buzz'},
{'loads':'fizz','of':'bazz','fields':'errrrm'}
]
Erstellt zwei Ressourcen mit diesen Details. Welches war schön.
Ich bin zu einer ähnlichen Schlussfolgerung wie Daniel Albarral gekommen, aber hier ist eine prägnantere Lösung:
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)
Ich konnte nicht recht herausfinden, wie ich die Anfrage erhielt. DATA, von einem Wörterbuch in ein Array zu konvertieren - was meine Fähigkeiten an Tom Manterfields Lösung eingeschränkt hat. Hier ist meine Lösung:
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)
Und dann führe ich das Äquivalent auf dem Client aus:
var things = {
"things":[
{'loads':'foo','of':'bar','fields':'buzz'},
{'loads':'fizz','of':'bazz','fields':'errrrm'}]
}
thingClientResource.post(things)
Hier ist eine andere Lösung, Sie müssen die __init__
-Methode der Serialisierer nicht überschreiben. Überschreiben Sie einfach die (ModelViewSet) 'create'
-Methode Ihrer Ansicht. Hinweis many=isinstance(request.data,list)
. Hier many=True
, wenn Sie ein Array von zu erstellenden Objekten senden, und False
, wenn Sie nur eines senden. Auf diese Weise können Sie sowohl einen Artikel als auch eine Liste speichern!
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)
Sie können einfach die get_serializer
-Methode in Ihrem APIView überschreiben und many=True
in get_serializer
der Basisansicht wie folgt übergeben:
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)
Ich denke, der beste Erfolg, um die vorgesehene Architektur des Frameworks zu respektieren, wird sein, eine Mischung wie diese zu erzeugen:
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)
Dann können Sie das CreateModelMixin von ModelViewSet folgendermaßen überschreiben:
class <MyModel>ViewSet(CreateListModelMixin, viewsets.ModelViewSet):
...
...
Jetzt können Sie im Client so arbeiten:
var things = [
{'loads':'foo','of':'bar','fields':'buzz'},
{'loads':'fizz','of':'bazz','fields':'errrrm'}
]
thingClientResource.post(things)
oder
var thing = {
'loads':'foo','of':'bar','fields':'buzz'
}
thingClientResource.post(thing)
BEARBEITEN:
Wie Roger Collins in ihrer Antwort vorschlägt, ist schlauer, die get_serializer-Methode zu überschreiben als die 'create'.
Die Generic Views - Seite in Django REST Frameworks Dokumentation gibt an, dass die generische View ListCreateAPIView "für Lese-Schreib-Endpunkte verwendet wird, um eine Sammlung von Modellinstanzen darzustellen".
Dort würde ich anfangen zu suchen (und das werde ich auch tun, da wir diese Funktionalität bald auch in unserem Projekt benötigen werden).
Beachten Sie auch, dass die Beispiele auf der Seite "Generic Views" ListCreateAPIView
verwenden.
Ich habe ein einfaches Beispiel in post
gefunden
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)
Diese Zeilen sind die eigentliche Logik von 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)
Wenn Sie mit vielen verwechselt werden = True, siehe dies
Wenn wir Daten senden, werden diese innerhalb von list
in etwa so sein -
[
{
"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"
}
]
Die einfachste Methode, auf die ich gestoßen bin:
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)