I would like to write a drf validator that will mark a field as required based on the value of an other field. For example:
class MySerializer(serializers.Serializer):
has_children = fields.BooleanField()
nb_childs = fields.IntegerField(min_value=1, validators=[RequiredIf(field='has_children', value=True)], required=False)
At first i believed the class based validator was the way to do it, by retrieving the value of 'has_children' with a method like this:
def set_context(self, serializer_field):
print serializer_field.parent.initial_data
but the 'initial_data' is not set. Any clue?
I am using several mixins for that purpose, which are changing field.required attribute and as result error validation messages are generated automatically by DRF
PerFieldMixin
class ConditionalRequiredPerFieldMixin:
"""Allows to use serializer methods to allow change field is required or not"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field_name, field in self.fields.items():
method_name = f'is_{field_name}_required'
if hasattr(self, method_name):
field.required = getattr(self, method_name)()
How to use PerFieldMixin
class MySerializer(ConditionalRequiredPerFieldMixin, serializers.ModelSerializer):
subject_id = serializers.CharField(max_length=128, min_length=3, required=False)
def is_subject_id_required(self):
study = self.context['study']
return not study.is_community_study
PerActionMixin
class ActionRequiredFieldsMixin:
"""Required fields per DRF action
Example:
PER_ACTION_REQUIRED_FIELDS = {
'update': ['notes']
}
"""
PER_ACTION_REQUIRED_FIELDS = None
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.context.get('view'):
action = self.context['view'].action
required_fields = (self.PER_ACTION_REQUIRED_FIELDS or {}).get(action)
if required_fields:
for field_name in required_fields:
self.fields[field_name].required = True
How to use PerActionMixin
see docstrings, for action == update (ie PUT request) - field "notes" will be required)
Have a look here in the DRF documentation
Basically, to do object-level validation, you need to override the Serializer's validate(self, data)
method, do your validation using the data
parameter's value (this is the serializer's state provided as a dict
to validate) then raise a ValidationError
if anything is wrong.
If you need to raise an error for a specific field, then you can pass a dictionary as the parameter to the ValidationError
constructor:
raise ValidationError({'yourfield': ['Your message']})
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With