100 votes

Comment créer plusieurs instances de modèle avec Django Rest Framework ?

Je voudrais enregistrer et mettre à jour plusieurs instances à l'aide du cadre Django Rest avec un seul appel d'API. Par exemple, disons que j'ai un modèle "Classroom" qui peut avoir plusieurs "Teachers". Si je veux créer plusieurs enseignants et mettre à jour ultérieurement tous leurs numéros de classe, comment dois-je procéder ? Dois-je lancer un appel d'API pour chaque enseignant ?

Je sais qu'actuellement nous ne pouvons pas sauvegarder les modèles imbriqués, mais j'aimerais savoir si nous pouvons le faire au niveau de l'enseignant. Merci !

93voto

Tom Manterfield Points 2840

Je sais que cette question a été posée il y a un certain temps, mais je l'ai trouvée en essayant de la résoudre moi-même.

Il s'avère que si vous passez many=True lors de l'instanciation de la classe de sérialiseur pour un modèle, elle peut alors accepter plusieurs objets.

Ceci est mentionné aquí dans la documentation du cadre de repos de django

Dans mon cas, ma vue ressemblait à ceci :

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

Je n'avais pas vraiment envie d'écrire un tas de textes passe-partout juste pour avoir un contrôle direct sur l'instanciation du sérialiseur et passer l'information à l'utilisateur. many=True Ainsi, dans ma classe de sérialiseur, je surcharge la fonction __init__ à la place :

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', )

Envoi de données à l'URL de la liste pour cette vue dans le format :

[
    {'loads':'foo','of':'bar','fields':'buzz'},
    {'loads':'fizz','of':'bazz','fields':'errrrm'}
]

Création de deux ressources avec ces détails. Ce qui était bien.

75voto

Roger Collins Points 63

Je suis arrivé à la même conclusion que Daniel Albarral, mais voici une solution plus succincte :

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)

61voto

waqmax Points 591

Voici une autre solution, vous n'avez pas besoin de surcharger vos serializers __init__ méthode. Il suffit de surcharger la méthode de votre vue (ModelViewSet) 'create' méthode. Avis many=isinstance(request.data,list) . Ici many=True lorsque vous envoyez un tableau d'objets à créer, et False quand vous n'en envoyez qu'un seul. De cette façon, vous pouvez sauvegarder à la fois un article et une liste !

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)

14voto

akaphenom Points 1692

Je n'ai pas réussi à faire en sorte que le fichier request.DATA soit converti d'un dictionnaire en un tableau, ce qui m'a empêché de faire fonctionner la solution de Tom Manterfield. Voici ma solution :

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)

Et ensuite j'exécute l'équivalent de ceci sur le client :

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

14voto

Daniel Albarral Points 73

Je pense que la meilleure approche pour respecter l'architecture proposée du framework sera de créer un mixin comme celui-ci :

class CreateListModelMixin(object):

    def create(self, request, *args, **kwargs):
        """
            Create a list of model instances if a list is provided 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)

Vous pouvez alors surcharger la fonction CreateModelMixin de ModelViewSet comme suit :

class <MyModel>ViewSet(CreateListModelMixin, viewsets.ModelViewSet):
    ...
    ...

Maintenant, dans le client, vous pouvez travailler comme ceci :

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

ou

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

thingClientResource.post(thing)

EDIT :

Comme le suggère Roger Collins dans sa réponse est plus judicieux d'écraser la méthode get_serializer que la méthode 'create'.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X