Skip to content

Instantly share code, notes, and snippets.

@gonzaloamadio
Created October 25, 2021 20:49
Show Gist options
  • Save gonzaloamadio/d42acb8cce05061e674a7f698f03972c to your computer and use it in GitHub Desktop.
Save gonzaloamadio/d42acb8cce05061e674a7f698f03972c to your computer and use it in GitHub Desktop.
OtherFieldValidator Django
class OtherFieldValidatorInSerializer:
"""
A validator to be inherited from, that will give use the
possibility of validate two fields.
More exaplanation on comments along the code.
TODO: Easily extensible to a list of fields
USAGE EXAMPLE:
(For a model that has to fields, date_start and date_end)
In the serializer definition, we can have something like:
from .validators import EndDateValidator
class JobSerializer(serializers.ModelSerializer):
class Meta:
model = Job
fields = '__all__'
extra_kwargs = {
'date_end': {'validators': [EndDateValidator('date_start')]},
}
"""
#### This part is the same for all validators ####
def __init__(self, other_field):
self.other_field = other_field # name of parameter
def set_context(self, serializer_field):
self.serializer_field = serializer_field # name of field where validator is defined
# TODO: make abc class, to force the implementation of this function.
def make_validation(self,field, other_field):
pass
def __call__(self, value):
field = value
serializer = self.serializer_field.parent # serializer of model
try:
# If <other_field> is not provided, here we will obtain the error
# django.utils.datastructures.MultiValueDictKeyError: <'other_field'>
# But we do not want to fail with that error. We want to fail for
# example if it is a requiered field, with "This field is required"
# If it is not a required field, and it was not provided, it does
# not have sense to validate against, so we skip this anyway.
raw_other_field = serializer.initial_data[self.other_field] # data del otro campo
# Run validators from other_field before running this one. If it
# does not pass the validations, it does not have sense to run
# current. So we have to first fix the other field.
other_field = serializer.fields[self.other_field].run_validation(raw_other_field)
#except ValidationError:
except:
# if date_start is incorrect or was not provided in initial data
# (sended in post api call for example) we will omit validating range
return
#### Here is the only part that changes ####
# We MUST override this function
self.make_validation(field,other_field)
+++++++++++++++++++++++++++++++++++++++++++ One possible implementation
# -*- coding: utf-8 -*-
from django.utils.translation import ugettext_lazy as _
from rest_framework.serializers import ValidationError
from tektank.libs.validators import OtherFieldValidatorInSerializer
class EndDateValidator(OtherFieldValidatorInSerializer):
"""Check that start date is before end date.
``class EndDateValidator(OtherFieldValidatorInSerializer)``
The class inherit from the validator that check some condition between to
fields. So we have to implement the make_validation function.
"""
def make_validation(self, field, other_field):
"""Implement the validation. Check that one date field is before another."""
date_end = field
date_start = other_field
if date_start and date_end and date_end < date_start:
raise ValidationError(
_('End date cannot be before start date.'), code='invalid_model_data',
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment