Как сделать вложенный сериализатор доступным внутри create() ModelSerializer, не делая вложенный сериализатор read_only=False
У меня небольшая дилемма. Задача состоит в том, чтобы принять json, содержащий некоторую информацию о помёте кроликов и родившихся крольчатах. У меня есть ThrowInfoSerializer с моделью Throw (Throw = Litter, не англоязычная страна). Внутри create ThrowInfoSerializer я хочу сохранить новый помет и соответствующих новых кроликов, поэтому я вызываю new_bunnies = validated_data.pop('new_bunnies') и хочу извлечь данные о новых кроликах из validated_data.
Проблема в том, что поскольку новые данные Bunny хранятся в другом ModelSerializer, который связан с моделью Bunny, и является BunnySerializer(many=True, read_only=True) read_only, то они не доступны в validated_data внутри create().
Но если я хочу сделать new_bunnies read_only=False, чтобы информация стала частью validated_data внутри create(), я получаю эту ошибку:
AttributeError: Got AttributeError when attempting to get a value for field `new_bunnies` on serializer `ThrowInfoSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `Throw` instance.
Original exception text was: 'Throw' object has no attribute 'new_bunnies'.
Что вполне логично, поскольку ThrowInfoSerializer привязан к Throw Model.
Я не могу сделать еще один сериализатор и поместить ThrowInfoSerializer и BunnySerializers на один уровень, поскольку мне все еще нужна Throw Info из ThrowInfoSerializer для сохранения новых кроликов.
Throw = Litter, мы испортили перевод
Вот код: Serializers.py
from rest_framework import serializers
from rest_framework.serializers import Serializer, ModelSerializer, SerializerMethodField
from bunny_service.bunny_utils import getStudBookKeeperOfUser
from bunny_service.models import Throw, Bunny
class BunnySerializer(ModelSerializer):
sex = serializers.CharField()
class Meta:
model = Bunny
fields = ['sex', 'RID', 'COLID']
class ParentBunnySerializer(ModelSerializer):
class Meta:
model = Bunny
fields = ['RID', 'COLID', 'l_ear', 'r_ear', 'UID_owner']
def validate(self, attrs):
# Check if a Bunny with the given attributes exists
if not Bunny.objects.filter(
RID=attrs.get('RID'),
COLID=attrs.get('COLID'),
l_ear=attrs.get('l_ear'),
r_ear=attrs.get('r_ear'),
UID_owner=attrs.get('UID_owner')
).exists():
raise serializers.ValidationError("A bunny with those properties does not exist")
return attrs
class ThrowInfoSerializer(ModelSerializer):
count = SerializerMethodField()
remaining = SerializerMethodField()
new_bunnies = BunnySerializer(many=True)
BID_buck = ParentBunnySerializer()
BID_doe = ParentBunnySerializer()
class Meta:
model = Throw
fields = ['thrown_on', 'covered_on', 'death_count', 'BID_buck', 'BID_doe', 'count', 'remaining', 'new_bunnies']
def get_count(self, instance):
return instance.bunny_set.count()
def get_remaining(self, instance):
return self.get_count(instance) - instance.death_count
def create(self, validated_data, **kwargs):
print(validated_data)
new_bunnies = validated_data.pop('new_bunnies')
BID_buck = Bunny.objects.get(**validated_data.pop('BID_buck', None))
BID_doe = Bunny.objects.get(**validated_data.pop('BID_doe', None))
validated_data['BID_buck'] = BID_buck
validated_data['BID_doe'] = BID_doe
validated_data['UID_stud_book_keeper'] = getStudBookKeeperOfUser(self.context['user'])
throw = Throw.objects.create(**validated_data)
print(new_bunnies)
for bunny in new_bunnies:
Bunny.objects.create(
**bunny,
TID=throw,
UID_owner=self.context['user'],
CID=self.context['user'].CID_breeding_club,
l_ear="TO BE IMPLEMENTED", #TODO: implement
r_ear="TO BE IMPLEMENTED"
)
print(Bunny.objects.all())
return throw
models.py
Надеюсь, это имеет смысл
Ошибка, которую вы получаете Original exception text was: 'Throw' object has no attribute 'new_bunnies'.
, связана с тем, что поля с именем new_bunnies
на самом деле не существует. Чтобы исправить это, вы можете:
Измените
new_bunnies
на название поля, которое реально существует в модели.Укажите, на какое поле
new_bunny
ссылается:
new_bunnies = UUIDField(source='the.referenced.field.in.the.model)
В случае необходимости навигации по отношениям в исходном поле, разделяйте их точками .
. И, конечно же, замените UUIDField
на тип поля, который соответствует данным, которые вы собираетесь получить.
- Или, если
new_bunnies
не является полем конкретной модели, вы можете просто определить его, как во втором варианте, но без поляsource
.
new_bunnies = UUIDField()
Таким образом вы определяете поле для сериализатора, хотя в модели его не существует. Но при таком подходе вам понадобится пользовательская функция create()
для обработки того, что происходит с этим новым полем, поскольку оно не является частью модели и django не будет знать, что с ним делать.
Надеюсь, это поможет вам.