web-dev-qa-db-fra.com

Comment PATCHER un seul champ en utilisant Django Rest Framework?

J'ai un modèle 'MyModel' avec plusieurs champs et j'aimerais mettre à jour un champ 'status' en utilisant la méthode PATCH J'utilise des vues basées sur les classes. Est-il possible d'implémenter PATCH? 

31
pnhegde

Les sérialiseurs autorisent les mises à jour partielles en spécifiant partial=True lors de l'initialisation du série. C’est ainsi que les requêtes PATCH sont traitées par défaut dans les vues génériques .

serializer = CommentSerializer(comment, data=request.data, partial=True)

Cela vous permettra de mettre à jour des champs individuels d'un sérialiseur, ou tous les champs si vous le souhaitez, sans aucune des restrictions d'une requête PUT standard.

28
Kevin Brown

Comme Kevin Brown a déclaré que vous pouvez utiliser le partial=True, qui chefarov a bien précisé.

Je voudrais juste les corriger et dire que vous pouvez utiliser les génériques librement, en fonction de la méthode HTTP que vous utilisez:

  • Si vous utilisez la méthode PATCH HTTP comme demandé, vous la récupérez. Vous pouvez voir le code UpdateModelMixin pour partial_update:

    def partial_update(self, request, *args, **kwargs):
        kwargs['partial'] = True
        return self.update(request, *args, **kwargs)
    
  • Pour toute méthode HTTP différente de PATCH, ceci peut être accompli en remplaçant simplement la méthode get_serializer comme suit:

    def get_serializer(self, *args, **kwargs):
        kwargs['partial'] = True
        return super(YOUR_CLASS, self).get_serializer(*args, **kwargs)
    

Cela va créer le sérialiseur comme partiel, et le reste des génériques fonctionnera comme un charme sans aucune intervention manuelle dans le mécanisme update/partial_update.

Sous la capuche

J'ai utilisé le générique: generics.UpdateAPIView qui utilise la UpdateModelMixin qui a ce code:

def update(self, request, *args, **kwargs):
    partial = kwargs.pop('partial', False)
    instance = self.get_object()
    serializer = self.get_serializer(instance, data=request.data, partial=partial)
    …

Ainsi, si vous remplacez la fonction get_serializer, vous pouvez en réalité remplacer l’argument partiel et le forcer à la valeur true.

Veuillez noter que si vous ne voulez utiliser que partiellement certaines méthodes HTTP, cela rend cette approche plus difficile.

J'utilise djangorestframework == 3.5.3

12
Uri Shalit

Il semble être pris en charge hors de la boîte. Dans l'API de votre navigateur, accédez à une page de détail du modèle. En bas de l'onglet HTML Form, cliquez sur Raw data, supprimez tout ce qui se trouve dans la chaîne JSON, à l'exception du champ ID et du champ que vous souhaitez modifier, puis cliquez sur PATCH. Une mise à jour partielle PATCH est effectuée.

J'utilise djangorestframework==3.2.4 et je n'ai rien à faire avec mes ViewSets et mes sérialiseurs pour l'activer.

Dans cet exemple, nous mettons à jour le champ bool status_field du modèle et j’utilise jquery 2.2.1. Ajoutez ce qui suit au <head>:

<script src="{% static 'my_app/jquery.min.js' %}"></script>
<script>
$(document).ready(function(){
    var chk_status_field = $('#status_field');
    chk_status_field.click(function(){
        $.ajax({url: "{% url 'model-detail' your_rendering_context.id %}",
            type: 'PATCH', timeout: 3000, data: { status_field: this.checked }
        })
        .fail(function(){
            alert('Error updating this model instance.');
            chk_status_field.prop('checked', !chk_status_field.prop('checked'));
        });
    });
});
</script>

Puis dans un <form>:

<input type="checkbox" id="status_field" {% if your_rendering_context.status_field %} 
    checked {% endif %} >

J'ai choisi de permettre à la case à cocher de changer, puis de la retourner en cas d'échec. Mais vous pouvez remplacer click par mousedown pour ne mettre à jour la valeur de case à cocher qu'une fois l'appel AJAX réussi. Je pense que cela incitera cependant les gens à cliquer plusieurs fois sur la case à cocher pour les connexions lentes.

5
Chris

Si quelqu'un envisage toujours de trouver une solution simple en utilisant ModelSerializer sans modifier la plupart de vos points de vue, vous pouvez sous-classer ModelSerializer et en faire hériter tous vos ModelSerializers.

class PatchModelSerializer(serializers.ModelSerializer):
    def __init__(self, *args, **kwargs):
        kwargs['partial'] = True
        super(PatchModelSerializer, self).__init__(*args, **kwargs)

class ArticleSerializer(PatchModelSerializer):
    class Meta:
        model = Article
3
firecast

J'ai eu du mal avec celui-ci pendant un moment, mais c'est une implémentation très simple qui utilise des vues génériques ou une combinaison de vues génériques et de mixins.

Si vous utilisez une vue de mise à jour générique (generics.UpdateAPIView), utilisez simplement le code suivant, en vous assurant que le type de demande est PATCH:

class UpdateUser(generics.UpdateAPIView):

    queryset = User.objects.all()
    serializer_class = UserSerializer

Il n'y a rien d'autre à cela!

Si vous utilisez un mix de mise à jour (mixins.UpdateModelMixin) associé à une vue générique (generics.GenericAPIView), utilisez le code suivant, en vous assurant que le type de demande est PATCH:

class ActivateUser(mixins.UpdateModelMixin, generics.GenericAPIView):

    serializer_class = UserSerializer
    model = User
    lookup_field = 'email'

    def get_queryset(self):
        queryset = self.model.objects.filter(active=False)
        return queryset

    def patch(self, request, *args, **kwargs):
        return self.partial_update(request, *args, **kwargs)

Le deuxième exemple est plus complexe et montre comment remplacer le champ queryset et le champ de recherche, mais le code auquel vous devez faire attention est la fonction patch.

0
user2583728