6đź‘Ť
Before validate()
method, DRF serializers call to_internal_value(self, data)
. You will get all data of parent serializer there. So as you defined validate()
method in serializer, define to_internal_value()
method and catch parent serializer’s data.
2đź‘Ť
You can access initial_data
on the parent serializer from the nested serializers validate()
method. I’ve also added some code for using the parent fields run_validation()
method, which would validate and return the internal value from to_internal_value()
, which might be a better than dealing with the initial data.
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
- Django: Display a custom error message for admin validation error
- Django: vps or shared hosting?
- Django Admin, accessing reverse many to many
- How to store an integer leaded by zeros in django
- Django: AttributeError: 'NoneType' object has no attribute 'split'
- Django – links generated with {% url %} – how to make them secure?
- Determine if Django is running under the development server
- Serializer validate function is not called DRF
1đź‘Ť
It might not be the best idea to do it this way, NestedSerializer
should not be aware of the parent object. It would make your code difficult to maintain, also it would make NestedSerializer dependent on OuterSerializer.
Instead, define a validate(self, data)
method in the OuterSerializer
and run the mutual validation there.
0đź‘Ť
Here’s what I’m doing now but I’m interested to see other answers..
Basically I’ve created a custom field for the field in the parent serializer that needs to be accessed in the child serializer – in this case “customer”. Then override to_internal_value()
to add the field’s validated data as an attribute on the parent serializer.
Once it’s been added as an attribute it can be accessed on the child serializer through self.parent.<attribute_name>
or on child serializer fields by 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,
)
- GAE and Django: What are the benefits?
- Python import as tuple
- Django: Limiting the number of relationships in a OnetoMany relationship
- Is npm in Node like virtualenv in Django?
0đź‘Ť
You are almost there!!!
Use self.parent.initial_data
to access the data given to the parent serializer.
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
0đź‘Ť
Do not hardcode the field_name
self.parent.initial_data[self.field_name]