There are two parts to this question:
- How do I go about validating multiple fields?
- How do I only validate a subset of fields for some input?
I will answer these two questions individually.
How do I go about validating multiple fields?
Django REST Framework allows you to validate multiple fields by overriding the validate method on the serializer. This is done last, after all other validators have run, and there is a detailed explanation in the documentation about it.
def validate(self, attrs):
if "type" in attrs:
the_type = attrs["type"]
if the_type == "A":
# Check if "a_field" was actually passed
if "a_field" not in attrs:
raise ValidationError({"a_field": self.error_messages["required"]})
# Check that it's "great"
if attrs["a_field"] != "great":
raise Va class DemoSerializer(serializers.Serializer):
type = serializers.ChoiceField(choices=types)
a_field = serializers.CharField(required=False)
def to_representation(self, instance):
optional_fields = {
"B": ["a_field"],
}
data = super(DemoSerializer, self).to_representation(instance)
instance_type = data["type"]
if instance_type in optional_fields:
remove_fields = optional_fields[instance_type]
for field in remove_fields:
if field in data:
data.pop(field)
return datalidationError({"a_field": self.error_messages["not_great"]})
return attrs
This will only validate a_field if type is "A".
How do I only validate a subset of fields for some input?
This is the tricky part, as by default Django REST Framework will not pass data through the serializer that has a key not in the fields list. So if, in our example, you didn't add a_field to the list, an error would always be triggered that the field is required, even if the data was passed in. This means you will need to add all of the optional fields to your serializer and mark them as required=False.
class DemoSerializer(serializers.Serializer):
type = serializers.ChoiceField(choices=types)
a_field = serializers.CharField(required=False)
This has the unfortunate side effect of making all of these optional fields show when the data is serialized using the serializer, but that is something you can fix by overriding to_representation. You will need to know what fields to remove ahead of time.
class DemoSerializer(serializers.Serializer):
type = serializers.ChoiceField(choices=types)
a_field = serializers.CharField(required=False)
def to_representation(self, instance):
optional_fields = {
"B": ["a_field"],
}
data = super(DemoSerializer, self).to_representation(instance)
instance_type = data["type"]
if instance_type in optional_fields:
remove_fields = optional_fields[instance_type]
for field in remove_fields:
if field in data:
data.pop(field)
return data
This should allow you to remove a list of fields based on the type that is returned.