webentwicklung-frage-antwort-db.com.de

Wie erstelle ich mit Django Rest Framework mehrere Modellinstanzen?

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.

48
Chaz

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.

58
Tom Manterfield

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)
31
Roger Collins

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)
11
akaphenom

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)
10
waqmax

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)
5
TehQuila

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'.

4
Daniel Albarral

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.

3
akaihola

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"
    }
]
1
Huzaif Sayyed

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)
0
popen