我有点进退两难。任务是接收一个 json,其中包含有关兔子窝和出生兔子的一些信息。我得到了一个带有 Throw 模型的 ThrowInfoSerializer(Throw = Litter,非英语国家)。在 ThrowInfoSerializer 的创建过程中,我想保存一个新的 Litter 和相应的新 Bunnies,因此我调用 new_bunnies = valid_data.pop('new_bunnies') 并希望从 valid_data 中提取新的 Bunny 数据。
问题是:由于新的 Bunny 数据存储在链接到 Bunny 模型的另一个 ModelSerializer 中,并且是 BunnySerializer(many=True, read_only=True) read_only,因此它在 create() 内的 valid_data 中不可用。
但是如果我想让 new_bunnies read_only=False 以使 create() 内的 valid_data 的信息部分成为我收到此错误:
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 模型相关联。
我无法真正制作另一个序列化器并将 ThrowInfoSerializer 和 BunnySerializers 放在同一级别,因为我仍然需要 ThrowInfoSerializer 的 Throw Info 来保存新的兔子。
Throw = Litter,我们搞砸了翻译
代码如下: 序列化器.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
模型.py
class Bunny(models.Model):
l_ear = models.CharField(max_length=50)
r_ear = models.CharField(max_length=50)
sex = models.CharField(max_length=1, choices=[('M', 'Male'), ('F', 'Female')])
tattooed_on = models.DateTimeField(null=True)
CID = models.ForeignKey(
Club,
on_delete=models.PROTECT,
blank=False
)
UID_owner = models.ForeignKey(
"auth_service.User",
on_delete=models.PROTECT,
blank=False,
related_name='bunny_owners'
)
TID = models.ForeignKey(
"bunny_service.Throw",
on_delete=models.PROTECT,
blank=True,
null=True
)
RID = models.ForeignKey(
"bunny_service.Race",
on_delete=models.PROTECT,
blank=False
)
COLID = models.ForeignKey(
"bunny_service.Color",
on_delete=models.PROTECT,
blank=False
)
UID_tattoo_master = models.ForeignKey(
"auth_service.User",
on_delete=models.PROTECT,
blank=False,
related_name='bunny_tattoo_master',
null=True
)
def __str__(self):
return self.l_ear + " / " + self.r_ear
class Throw(models.Model):
covered_on = models.DateTimeField()
thrown_on = models.DateTimeField()
death_count = models.IntegerField()
reported_on = models.DateTimeField(null=True, blank=True)
BID_buck = models.ForeignKey( # buck = Rammler
Bunny,
on_delete=models.PROTECT,
related_name='bunny_buck'
)
BID_doe = models.ForeignKey( # doe = Häsin
Bunny,
on_delete=models.PROTECT,
related_name='bunny_doe'
)
UID_stud_book_keeper = models.ForeignKey(
User,
on_delete=models.PROTECT,
blank=False
)
def __str__(self):
return str(self.BID_buck.UID_owner) + " / "+ str(self.thrown_on)
class Color(models.Model):
name = models.CharField(max_length=100)
abbreviation = models.CharField(max_length=50)
def __str__(self):
return self.name
class Race(models.Model):
name = models.CharField(max_length=100)
abbreviation = models.CharField(max_length=50)
SID = models.ForeignKey(
"Section",
on_delete=models.PROTECT,
blank=False
)
def __str__(self):
return self.name
class User(AbstractUser):
"""
Custom KEZ User supporting email authentication instead of username
"""
email = models.EmailField(unique=True, null=False, blank=False)
username = None
first_name = models.CharField(max_length=50, null=True, blank=True)
last_name = models.CharField(max_length=50, null=False, blank=False)
date_of_birth = models.DateField(null=True, blank=True)
CID_breeding_club = models.ForeignKey(
to='club_service.Club',
on_delete=models.SET_NULL,
null=True,
blank=True,
)
# Address
street = models.CharField(max_length=50, null=True, blank=True)
zip_code = models.PositiveSmallIntegerField(null=True, blank=True) # TODO: validate length
city = models.CharField(max_length=50, null=True, blank=True)
state = models.CharField(max_length=50, null=True, blank=True)
country = models.CharField(max_length=50, null=True, blank=True)
telephone = PhoneNumberField(null=True, blank=True)
is_active = models.BooleanField(default=False)
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['last_name']
def __str__(self):
return self.email
我希望这是有道理的
您收到的错误
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 不知道如何处理它。
希望这对您有帮助。