ModelViewSet - обновление вложенного поля
В настоящее время я работаю над Django с Django Rest Framwork.
Я не могу обновить свой объект внутри поля вложенных объектов.
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)
Когда я это сделаю:
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"
}
}
Я получаю следующий ответ:
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.
Зная:
- Я хочу сохранить сериализацию владельца в GET;
- Мы можем представить себе автомобиль, вложенный другим объектом и ect...
Как я могу сделать, если я хочу изменить владельца, когда я обновляю автомобиль.
Ответы
Ответ 1
Немного поздно, но, попробуй,
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
В любом случае кто-то спотыкается над этим
имел ту же ошибку в моем случае, но установил read_only, чтобы True зафиксировал это для меня.
owner = ownerSerializer(many=False, read_only=True)
Обратите внимание, что это поле не будет отображаться в форме при отправке данных в api.
Ответ 3
Источник ModelSerializer.create()
и ModelSerializer.update()
запрещает это поведение:
raise_errors_on_nested_writes('create', self, validated_data)
Если мы удалим эту строку, действие может пройти и работать хорошо.
Это означает, документ говорит:
Чтобы использовать записываемую вложенную сериализацию, вы хотите объявить вложенное поле в классе сериализатора и явно написать методы create()
и/или update()
.
Мы можем напрямую использовать исходный код ModelSerializer.save()
и ModelSerializer.update()
для этого метода "write explicit" с удалением строки raise_errors_on_nested_writes
.
Таким образом, над всем, мы можем определить такой класс AllowNestedWriteMixin
, как показано ниже:
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
И если мы хотим, чтобы поведение было выполнено, мы наследуем этот класс mixin перед ModelSerializer, что-то вроде приведенного ниже в вашем примере:
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')