我正在阅读Django REST Framework,我有一个使用SerializerMethodField()使用getter序列化的模型。
但是,当我POST到此端点时,我希望能够设置此字段,但这不起作用,因为正如上面的文档所示,您无法写入SerializerMethodField。 Django REST中是否有任何方法可以为您定义自定义getter方法的序列化器字段以及自定义setter方法?
编辑:这是我想要做的事情的来源。客户与用户具有一对一的关系。class ClientSerializer(serializers.ModelSerializer):
email = serializers.SerializerMethodField()
def create(self, validated_data):
email = validated_data.get("email", None) # This doesn't work because email isn't passed into validated_data because it's a readonly field
# create the client and associated user here
def get_email(self, obj):
return obj.user.email
class Meta:
model = Client
fields = (
"id",
"email",
)
答案 0 :(得分:4)
您需要使用其他类型的字段:
class ClientSerializer(serializers.ModelSerializer):
email = serializers.EmailField(source='user.email')
def create(self, validated_data):
# DRF will create object {"user": {"email": "inputed_value"}} in validated_date
email = validated_data.get("user", {}).get('email')
class Meta:
model = Client
fields = (
"id",
"email",
)
答案 1 :(得分:4)
这是读/写序列化器方法字段:
class ReadWriteSerializerMethodField(serializers.SerializerMethodField):
def __init__(self, method_name=None, **kwargs):
self.method_name = method_name
kwargs['source'] = '*'
super(serializers.SerializerMethodField, self).__init__(**kwargs)
def to_internal_value(self, data):
return {self.field_name: data}
答案 2 :(得分:1)
就我而言,我需要get_*
方法内部的逻辑,并且无法使用source
属性获取值。所以我想出了这个领域。
class WritableSerializerMethodField(serializers.SerializerMethodField):
def __init__(self, method_name=None, **kwargs):
super().__init__(**kwargs)
self.read_only = False
def get_default(self):
default = super().get_default()
return {
self.field_name: default
}
def to_internal_value(self, data):
return {self.field_name: data}
答案 3 :(得分:0)
为什么不在视图中创建客户端呢?
def post(self, request, *args, **kwargs):
data = {
'email': request.data.get('email'),
}
serializer = ClientSerializer(data=data)
if serializer.is_valid():
email = serializer.data.get('email')
client = Client.objects.create(email=email)
# do other stuff
答案 4 :(得分:0)
我遇到了同样的问题,并提出了以下解决方案。
请注意,我确实需要在序列化程序中使用map <- leaflet() %>% addTiles()
for( level in rtc$level){
map <- addPolylines(map, data=subset(rtc, level==level))
}
# to show the map
map
,因为我需要根据SerializerMethodField
和某些权限填充字段,这对于request.user
来说太复杂了,或其他答案中提出的其他解决方案。
解决方案是“劫持” API视图的SerializerField
,并在那一点上执行特定的写操作(在我的情况下,使用普通序列器之上的另一个序列化器)。我只需要在更新中执行此操作,但是如果这是您的用例,则可能需要使用perform_update
进行。
它是这样的:
perform_create
我必须承认,按照MVC模式,它并不理想,但它可以工作。
答案 5 :(得分:0)
您可以在序列化程序上覆盖save()
方法,并使用self.initial_data
。然后,您需要自己对该字段进行验证。
class MySerializer(serializers.ModelSerializer):
magic_field = serializers.SerializerMethodField()
def get_magic_field(self, instance):
return instance.get_magic_value()
def save(self, **kwargs):
super().save(**kwargs) # This creates/updates `self.instance`
if 'magic_field' in self.initial_data:
self.instance.update_magic_value(self.initial_data['magic_field'])
return self.instance
答案 6 :(得分:0)
我尝试使用Guilherme Nakayama da Silva和Julio Marins的答案来解决我写给SerializerMethodField的问题。它适用于读取和更新,但不适用于创建。
所以我根据他们的回答创建了自己的WritableSerializerMethodField,它非常适合阅读,创建和编写。
class WritableSerializerMethodField(serializers.SerializerMethodField):
def __init__(self, **kwargs):
self.setter_method_name = kwargs.pop('setter_method_name', None)
self.deserializer_field = kwargs.pop('deserializer_field')
super().__init__(**kwargs)
self.read_only = False
def bind(self, field_name, parent):
retval = super().bind(field_name, parent)
if not self.setter_method_name:
self.setter_method_name = f'set_{field_name}'
return retval
def get_default(self):
default = super().get_default()
return {
self.field_name: default
}
def to_internal_value(self, data):
value = self.deserializer_field.to_internal_value(data)
method = getattr(self.parent, self.setter_method_name)
return {self.field_name: self.deserializer_field.to_internal_value(method(value))}
然后我在序列化程序中使用了它
class ProjectSerializer(serializers.ModelSerializer):
contract_price = WritableSerializerMethodField(deserializer_field=serializers.DecimalField(max_digits=12, decimal_places=2))
def get_contract_price(self, project):
return project.contract_price
def set_contract_price(self, value):
return value
答案 7 :(得分:-1)
您可以重复email
字段,虽然可以使用,但可能会造成混淆
class ClientSerializer(serializers.ModelSerializer):
email = serializers.SerializerMethodField()
email = serializers.EmailField(required=False)
def create(self, validated_data):
email = validated_data.get("email", None) # This doesn't work because email isn't passed into validated_data because it's a readonly field
# create the client and associated user here
def get_email(self, obj):
return obj.user.email
class Meta:
model = Client
fields = (
"id",
"email",
)