2015-01-07 14 views
12

Obecnie pracuję nad Django z Django Rest Framwork.ModelViewSet - aktualizacja zagnieżdżonego pola

Nie mogę zaktualizować obiektu w obrębie obiektu zagnieżdżonego.


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) 

Kiedy to zrobić:

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" 
    } 
} 

uzyskać następujące odpowiedzi:

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. 

wiedzieć:

  • chcę zachować szeregowanie właściciela na get;
  • Możemy sobie wyobrazić samochód zagnieżdżony przez inny obiekt i ect ...

Jak mogę zrobić, jeśli chcę zmienić właściciela kiedy zaktualizować samochód.

+2

możliwy duplikat [django-rest-framework-framework 3.0 tworzenia lub aktualizacji w zagnieżdżonym serializatorze] (http://stackoverflow.com/questions/27434593/django-rest-framework-3-0-create-or-update -in-nested-serializer) –

+0

Czołgi! Powtarzam, przepraszam, ale dlaczego zastąpienie jest w serializatorze, a nie w widoku, jak: [Django-doc] (http://www.django-rest-framework.org/api-guide/viewsets/#marking- dodatkowe akcje routingu), w ten sposób można zwrócić status? –

Odpowiedz

3

ktoś właśnie w sprawie, natyka się na ten

miał ten sam błąd w moim przypadku, ale ustawienie READ_ONLY True stałe to dla mnie.

owner = ownerSerializer(many=False, read_only=True) 

Należy zauważyć, że to pole nie pojawi się w formularzu podczas publikowania danych w interfejsie API.

6

Trochę późno, ale spróbuj tego,

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

Źródłem ModelSerializer.create() i ModelSerializer.update() zakazać tego zachowania:

raise_errors_on_nested_writes('create', self, validated_data) 

Gdybyśmy usunąć tę linię, akcja może dotrzeć i prace dobrze.

Oznacza to, the doc mówi:

Aby użyć zapisu zagnieżdżony serializacji będziesz chciał zadeklarować pola zagnieżdżone w klasie serializer i napisać create() i/lub update() metod wyraźnie.

Możemy użyć bezpośrednio kodu ModelSerializer.save() i ModelSerializer.update() źródłowego do tej „wyraźnej” metody zapisu, z raise_errors_on_nested_writes linii usunięte.

więc nad wszystkim, możemy zdefiniować taką AllowNestedWriteMixin klasy jak poniżej:

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 

A jeśli chcemy zachowanie dalej, dziedziczy tej klasy wstawionej przed ModelSerializer, coś jak poniżej na przykładzie:

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

Czy możesz dodać import? Nie widzę, skąd pochodzi metoda set_many. Dzięki! – Lukas

+0

Stosując to podejście, po prostu zignorujesz ten problem, pole 'owner' nie zostanie zaktualizowane –

Powiązane problemy