Reputation: 1080
Does anyone know how to OR together Django model field validators?
Something like this:
example_field = models.CharField(max_length=255, validators=[validator1|validator2])
I am guessing that there is a way and it involves the Q operator, but I can't find what it is exactly.
Upvotes: 1
Views: 127
Reputation: 546
I needed an OR-validator as well, so I made a little reusable validator that can accept any number of sub-validators that are OR-ed together. Inspired by Django's own validators, so should work everywhere (only tested in Django Rest Framework serializer)
from django.core.exceptions import ValidationError
from django.core.validators import EmailValidator, RegexValidator
from django.utils.deconstruct import deconstructible
from rest_framework import serializers
@deconstructible # allows usage in migrations
class OrValidator:
message = 'Enter a valid value.' # customize this based on the sub-validators
code = 'invalid'
def __init__(self, validators, message=None, code=None):
self.validators = validators
self._errors = []
if code is not None:
self.code = code
if message is not None:
self.message = message
def __call__(self, value):
for validator in self.validators:
try:
return validator(value)
except ValidationError as e:
# save error for debugging
self._errors.append(e)
# non matched, raise error
raise ValidationError(self.message, code=self.code)
def __eq__(self, other):
return (
self.validators == other.validators and
isinstance(other, self.__class__) and
self.message == other.message and
self.code == other.code
)
class UsernameValidator(RegexValidator):
regex = re.compile(r'^[-\w]+\Z', re.UNICODE)
# example usage in Django Rest Framework (should work in forms and models as well)
class ResetPasswordSerializer(serializers.Serializer):
email_or_username = serializers.CharField(
required=True,
validators=[
OrValidator(
[EmailValidator(), UsernameValidator()],
message='Enter a valid email or username',
),
],
)
Upvotes: 0
Reputation: 476528
You can do the validation in a function itself:
from django.core.exceptions import ValidationError
from django.db import models
def combined_validator(value):
try:
return validator1(value)
except ValidationError:
return validator2(value)
class MyModel(models.Model):
example_field = models.CharField(
max_length=255,
validators=[combined_validator]
)
If validator1
does not detect any trouble, then the control flow is returned, and thus we are safe. If it raises a ValidationError
, then we fallback on validator2
. If that does not raises an error, then we are again safe. Otherwise, the error will raise out of combined_validator
.
Upvotes: 3