DRF:从嵌套的序列化器验证方法中检索外部输入数据

时间:2016-12-26 10:45:38

标签: django-rest-framework

我正在使用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方法访问这些输入数据。有什么建议?谢谢你的帮助:)。

7 个答案:

答案 0 :(得分:1)

validate()方法之前,DRF序列化程序会调用to_internal_value(self, data)。您将获得父序列化程序的所有数据。因此,当您在序列化程序中定义validate()方法时,请定义to_internal_value()方法并捕获父序列化程序的数据。

答案 1 :(得分:0)

这是我现在正在做的事情,但我有兴趣查看其他答案。

基本上,我已经为需要在子序列化程序中访问的父序列化程序中的字段创建了一个自定义字段-在本例中为“客户”。然后覆盖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,
        )

答案 2 :(得分:0)

您可以从嵌套序列化器initial_data方法访问父序列化器上的validate()。我还添加了一些使用父字段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

答案 3 :(得分:0)

你快到了!!!

使用 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

答案 4 :(得分:0)

尝试self.root.instance以在嵌套序列化器中获取父实例。

答案 5 :(得分:0)

以这种方式进行操作可能不是最好的主意,NestedSerializer应该不知道父对象。这将使您的代码难以维护,也将使NestedSerializer依赖于OuterSerializer。

相反,请在validate(self, data)中定义一个OuterSerializer方法,然后在此处运行相互验证。

答案 6 :(得分:0)

请勿对字段名进行硬编码

self.parent.initial_data[self.field_name]