我正在使用django-rest-framework的序列化器。我有两个连续化器一个嵌套。
class NestedSerializer(serializers.Serializer):
value = AttributeValueField(required=True)
name = serializers.CharField(required=True)
class OuterSerializer(serializers.Serializer):
info = serializers.CharField()
nested = NestedSerializer()
为了验证嵌套序列化器的数据,我需要从父序列机中检索输入数据,类似的内容:
class NestedSerializer(serializers.Serializer):
...
def validate(self, data):
# of course, it doesn't work, but thats the idea.
info = self.parent.info
# then validate the NestedSerializer with info.
我找不到从验证方法访问这些输入数据的任何方法。有什么建议么?感谢您的帮助:)。
在validate()
方法之前,DRF序列化器调用to_internal_value(self, data)
。您将在那里获取父序列机的所有数据。因此,当您定义了序列化器中的validate()
方法时,定义to_internal_value()
方法并捕获Parent Serializer的数据。
您可以从嵌套序列化器validate()
方法上访问父序列化合物的initial_data
。我还添加了一些用于使用父字段run_validation()
方法的代码,该方法将验证并返回to_internal_value()
的内部值,这可能比处理初始数据更好。
class NestedSerializer(serializers.Serializer):
def validate(self, data):
# Retrieve the initial data, perhaps this is all you need.
parent_initial_data = self.parent.initial_data
info = parent_initial_data.get("info", None)
# Get the corresponding field and use `run_validation` or `to_internal_value` if needed
if info:
info_field = self.parent.fields["info"]
info = info_field.run_validation(info)
# info = info_field.to_internal_value(info) # If you don't want validation, but do want the internal value
# Do your thing
return data
尝试self.root.instance
以在嵌套的序列化器中获取父实例。
这可能不是最好的想法,NestedSerializer
不应意识到父对象。这将使您的代码难以维护,这也将使嵌套的器依赖于OuterSerializer。
相反,在OuterSerializer
中定义validate(self, data)
方法并在那里运行相互验证。
这是我现在正在做的事情,但我很想看到其他答案。
基本上,我已经为父序列机中的字段创建了一个自定义字段,该字段需要在子序列机中访问 - 在这种情况下为"客户"。然后覆盖to_internal_value()
以将字段的验证数据添加为父序列机上的属性。
作为属性添加后,可以通过self.parent.<attribute_name>
或self.root.<attribute_name>
class CustomerField(serializers.PrimaryKeyRelatedField):
def to_internal_value(self, data):
# Set the parent serializer's `customer` attribute to the validated
# Customer object.
ret = super().to_internal_value(data)
self.parent.customer = ret
return ret
class DebitField(serializers.PrimaryKeyRelatedField):
default_related_name = {
'OnAccount': 'onaccounts',
'Order': 'orders'
}
def get_queryset(self):
# Method must be overridden so the `queryset` argument is not required.
return super().get_queryset()
def set_queryset_from_context(self, model_name):
# Override the queryset depending on the model name.
queryset = self.default_related_name[model_name]
self.queryset = getattr(self.parent.customer, queryset)
def to_internal_value(self, data):
# Get the model from the `debit_type` and the object id from `debit`
# then validate that the object exists in the related queryset.
debit_type = data.pop('debit_type')
self.set_queryset_from_context(debit_type)
super().to_internal_value(data)
class PaymentLineSerializer(serializers.ModelSerializer):
debit = DebitField()
class Meta:
model = PaymentLine
fields = (
'id',
'payment',
'debit_type',
'debit', # GenericForeignKey
'amount',
)
def to_internal_value(self, data, *args):
data['debit'] = {
'debit': data.pop('debit'),
'debit_type': data.pop('debit_type'),
}
ret = super().to_internal_value(data)
return ret
def to_representation(self, instance):
data = super().to_representation(instance)
data['debit'] = instance.debit._meta.object_name
return data
class PaymentSerializer(serializers.ModelSerializer):
customer = CustomerField(queryset=Customer.objects.all())
class Meta:
model = Payment
fields = (
'id',
'customer',
'method',
'type',
'date',
'num_ref',
'comment',
'amount',
)
def __init__(self, *args, **kwargs):
self.customer = None
super().__init__(*args, **kwargs)
self.fields['lines'] = PaymentLineSerializer(
context=self.context,
many=True,
write_only=True,
)
你几乎在那里!
使用 self.parent.initial_data
访问给出的数据给出的数据。
class NestedSerializer(serializers.Serializer):
value = AttributeValueField(required=True)
name = serializers.CharField(required=True)
def validate(self, attrs):
attrs = super().validate(attrs)
the_input_data = self.parent.initial_data
info = the_input_data['info'] # this will not be the "validated data
# do something with your "info"
return attrs
不要硬码field_name
self.parent.initial_data[self.field_name]