10

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?

2 Answers 2

16

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']}) 
Sign up to request clarification or add additional context in comments.

3 Comments

Sorry but it's not what i asked for, I want a Validator. overriding validate wont do since the ValidationError will not be associated with the field.
You can raise field-level errors by passing a dictionary in the ValidationError constructor: raise ValidationError({'yourfield': ['Error message']})
sorry i didn't know about that. That should work for me, thanks
8

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)

2 Comments

I would like to know how you get self.context['study'] in the example. For me this is giving a KeyError even on GET requests. I think it should be coming from either data or cleaned_data, right?
You could pass some extra data (context) during serializer initialization that would later be available inside this serializer. DRF docs: django-rest-framework.org/api-guide/serializers/…

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.