2015-01-07 9 views
12

Ich arbeite gerade an Django mit Django Rest Framwork.ModelViewSet - Verschachteltes Feld aktualisieren

Ich kann mein Objekt im verschachtelten Objektfeld nicht aktualisieren.


serializer.py

class OwnerSerializer(serializers.ModelSerializer): 
    class Meta: 
     model = Owner 
     fields = ('id', 'name') 

class CarSerializer(serializers.ModelSerializer): 
    owner = ownerSerializer(many=False, read_only=False) 
    class Meta: 
     model = Car 
     fields = ('id', 'name', 'owner') 

view.py

class OwnerViewSet(viewsets.ModelViewSet): 
    queryset = Owner.objects.all() 
    serializer_class = OwnerSerializer 

class CarViewSet(viewsets.ModelViewSet): 
    serializer_class = CarSerializer 
    queryset = Car.objects.all() 

    def create(self, request): 
     serialized = self.serializer_class(data=request.DATA) 
     if serialized.is_valid(): 
      serialized.save() 
      return Response(status=HTTP_202_ACCEPTED) 
     else: 
      return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) 

Wenn ich dies tun:

Request URL:http://localhost:9000/api/v1/cars/1/?format=json 
Request Method:PUT 
Request Paylod : 
{ 
    "id":1, 
    "name": "TEST", 
    "ower": { 
     "id":1, 
     "name": "owner_test" 
    } 
} 

bekomme ich folgende Antwort:

The `.update()` method does not support writable nestedfields by default. 
Write an explicit `.update()` method for serializer `app.serializers.CarSerializer`, 
or set `read_only=True` on nested serializer fields. 

Wissen:

  • ich den Besitzer Serialisierung auf GET halten wollen;
  • Wir können das Auto von einem anderen Objekt verschachtelt vorstellen und ect ...

Wie kann ich tun, wenn ich den Besitzer wechseln soll, wenn ich das Auto aktualisieren.

+2

möglich duplizieren von [django-rest-framework 3.0 erstellen oder aktualisieren in verschachtelten Serializer] (http://stackoverflow.com/questions/27434593/django-rest-framework-3-0-create-or-update -in-nested-serializer) –

+0

Tanks! Ich wiederhole mich selbst, tut mir leid, aber warum ist die Überschreibung im Serializer und nicht in der Ansicht, wie: [Django-doc] (http://www.django-rest-framework.org/api-guide/viewsets/#marking- Extra-Aktionen-für-Routing, auf diese Weise kann Status zurückgeben ?? –

Antwort

3

Gerade im Fall, dass jemand stolpert auf diesen

die gleichen Fehler in meinem Fall hatte aber read_only auf True festgelegt es für mich zu setzen.

Beachten Sie, dass dieses Feld nicht im Formular angezeigt wird, wenn Daten in die API gebucht werden

6

ein wenig spät, aber, versuchen Sie dies,

class OwnerSerializer(serializers.ModelSerializer): 
    class Meta: 
     model = Owner 
     fields = ('id', 'name') 
     extra_kwargs = { 
      'id': { 
       'read_only': False, 
       'required': True 
      } 
     } #very important 

    def create(self, validated_data): 
     # As before. 
     ... 

    def update(self, instance, validated_data): 
     # Update the instance 
     instance.some_field = validated_data['some_field'] 
     instance.save() 

     # Delete any detail not included in the request 
     owner_ids = [item['owner_id'] for item in validated_data['owners']] 
     for owner in cars.owners.all(): 
      if owner.id not in owner_ids: 
       owner.delete() 

     # Create or update owner 
     for owner in validated_data['owners']: 
      ownerObj = Owner.objects.get(pk=item['id']) 
      if ownerObje: 
       ownerObj.some_field=item['some_field'] 
       ....fields... 
      else: 
       ownerObj = Owner.create(car=instance,**owner) 
      ownerObj.save() 

     return instance 
-2

Die Quelle von ModelSerializer.create() und ModelSerializer.update() dieses Verhalten verbieten:

raise_errors_on_nested_writes('create', self, validated_data) 

Wenn wir diese Linie entfernen, kann die Aktion durchkommen und Arbeiten Gut.

Es bedeutet, the doc sagt:

beschreibbaren verschachtelte Serialisierung verwenden Sie wollen ein verschachteltes Feld auf der Serializer Klasse deklarieren, und schreiben Sie die create() und/oder explizit update() Methoden.

können wir verwenden, um direkt den ModelSerializer.save() und ModelSerializer.update() Quellcode zu dieser „schreiben explicit“ Methode, mit der raise_errors_on_nested_writes Linie entfernt.

So über alle können wir, wie unten eine solche AllowNestedWriteMixin Klasse definieren:

import traceback 
from rest_framework.utils import model_meta 
from rest_framework.compat import set_many 

class AllowNestedWriteMixin: 
    def create(self, validated_data): 
     ModelClass = self.Meta.model 
     info = model_meta.get_field_info(ModelClass) 
     many_to_many = {} 
     for field_name, relation_info in info.relations.items(): 
      if relation_info.to_many and (field_name in validated_data): 
       many_to_many[field_name] = validated_data.pop(field_name) 

     try: 
      instance = ModelClass.objects.create(**validated_data) 
     except TypeError: 
      tb = traceback.format_exc() 
      msg = (
       'Got a `TypeError` when calling `%s.objects.create()`. ' 
       'This may be because you have a writable field on the ' 
       'serializer class that is not a valid argument to ' 
       '`%s.objects.create()`. You may need to make the field ' 
       'read-only, or override the %s.create() method to handle ' 
       'this correctly.\nOriginal exception was:\n %s' % 
       (
        ModelClass.__name__, 
        ModelClass.__name__, 
        self.__class__.__name__, 
        tb 
       ) 
      ) 
      raise TypeError(msg) 

     # Save many-to-many relationships after the instance is created. 
     if many_to_many: 
      for field_name, value in many_to_many.items(): 
       set_many(instance, field_name, value) 

     return instance 

    def update(self, instance, validated_data): 
     info = model_meta.get_field_info(instance) 

     for attr, value in validated_data.items(): 
      if attr in info.relations and info.relations[attr].to_many: 
       set_many(instance, attr, value) 
      else: 
       setattr(instance, attr, value) 
     instance.save() 

     return instance 

Und wenn wir das Verhalten auf möchten, erbt wir diese mixin Klasse vor ModelSerializer, so etwas wie die unten auf Ihrem Beispiel:

class OwnerSerializer(AllowNestedWriteMixin, 
         serializers.ModelSerializer): 
    class Meta: 
     model = Owner 
     fields = ('id', 'name') 

class CarSerializer(AllowNestedWriteMixin, 
        serializers.ModelSerializer): 
    owner = ownerSerializer(many=False, read_only=False) 
    class Meta: 
     model = Car 
     fields = ('id', 'name', 'owner') 
+0

Können Sie die Importe hinzufügen? Ich kann nicht sehen, woher die Methode set_many kommt. Vielen Dank! – Lukas

+0

Bei dieser Vorgehensweise ignorieren Sie einfach das Problem, das Feld "owner" wird nicht aktualisiert –

Verwandte Themen