Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django DRF Serializer fields required conditionally

I have a serializer which looks like this:

class MySerializer(serializers.Serializer):
    lat = serializers.FloatField(required=False)
    lng = serializers.FloatField(required=False)

I want to make sure that if a request comes with the 'lat' field, the 'lng' field will be mandatory and vice versa.

I can do it with validate function but believe there is a better way of doing that.

like image 538
Nuno_147 Avatar asked Apr 12 '18 21:04

Nuno_147


2 Answers

Here is an example of how to write a mixin to Serializer, which will allow conditionally required fields. See docstring for an example

class ConditionalRequiredMixin:
    """
    Adds flexibility to required fields by setting up `conditional_required_fields`

    For example
        conditional_required_fields = [
        ('is_blind': {
            'condition': False,
            'required_fields': ['clock', 'naming', 'alternate_trail_making', 'cube_drawing']
        }),
        ('is_blind': {
            'condition': True,
            'required_fields': ['clock2', 'naming2',]
        })
    ]

    If is_blind will be False -> fields specified in required_fields will be required

    """
    REQUIRED_MSG = ['Field is required']
    conditional_required_fields = []

    def validate(self, attrs):
        attrs = super().validate(attrs)
        for master_field, conditions in self.conditional_required_fields:
            master_field_value = attrs.get(master_field)
            condition = conditions['condition']
            if not isinstance(condition, str) and isinstance(condition, collections.Iterable):
                trigger = master_field_value in condition
            else:
                trigger = master_field_value == condition

            if trigger:
                additional_required_fields = conditions['required_fields']
                absent_fields = [f for f in additional_required_fields if attrs.get(f) is None]
                if absent_fields:
                    error_dict = {f: self.REQUIRED_MSG for f in absent_fields}
                    raise serializers.ValidationError(error_dict)
        return attrs
like image 129
pymen Avatar answered Nov 13 '22 01:11

pymen


You should use the serializer validate method to check this.

class MySerializer(serializers.ModelSerializer):
    lat = serializers.FloatField(required=False)
    lng = serializers.FloatField(required=False)

    def validate(self, data):
        if not data['lat'] and not data['lng']:
            raise serializers.ValidationError("They are both required.")
    return data

Reference: http://www.django-rest-framework.org/api-guide/serializers/#object-level-validation

like image 43
Luan Fonseca Avatar answered Nov 13 '22 00:11

Luan Fonseca