Table of Contents
Background
This package aims to provide tools needed to define custom field validation logic which can be used independently or with
django forms, test cases, API implementation or any model operation that requires saving data to the database.
This can also be extended by defining check constraints if needed but currently validation
will only be handled at the model level.
Installation
pip install django-extra-field-validation
Usage
Require all fields
from django.db import models
from extra_validator import FieldValidationMixin
class TestModel(FieldValidationMixin, models.Model):
amount = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True)
fixed_price = models.DecimalField(max_digits=7, decimal_places=2, null=True, blank=True)
percentage = models.DecimalField(max_digits=3, decimal_places=0, null=True, blank=True)
REQUIRED_FIELDS = ['amount']
Example
In [1]: from decimal import Decimal
In [2]: from demo.models import TestModel
In [3]: TestModel.objects.create(fixed_price=Decimal('3.00'))
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
...
ValueError: {'amount': ValidationError([u'Please provide a value for: "amount".'])}
Require at least one field in a collection
from django.db import models
from extra_validator import FieldValidationMixin
class TestModel(FieldValidationMixin, models.Model):
amount = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True)
fixed_price = models.DecimalField(max_digits=7, decimal_places=2, null=True, blank=True)
percentage = models.DecimalField(max_digits=3, decimal_places=0, null=True, blank=True)
REQUIRED_TOGGLE_FIELDS = [
['amount', 'fixed_price', 'percentage'],
]
Example
In [1]: from decimal import Decimal
In [2]: from demo.models import TestModel
In [3]: TestModel.objects.create(amount=Decimal('2.50'), fixed_price=Decimal('3.00'))
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
...
ValueError: {'fixed_price': ValidationError([u'Please provide only one of: Amount, Fixed price, Percentage'])}
Optionally require at least one field in a collection
from django.db import models
from extra_validator import FieldValidationMixin
class TestModel(FieldValidationMixin, models.Model):
amount = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True)
fixed_price = models.DecimalField(max_digits=7, decimal_places=2, null=True, blank=True)
percentage = models.DecimalField(max_digits=3, decimal_places=0, null=True, blank=True)
OPTIONAL_TOGGLE_FIELDS = [
['fixed_price', 'percentage']
]
Example
In [1]: from decimal import Decimal
In [2]: from demo.models import TestModel
In [3]: first_obj = TestModel.objects.create(amount=Decimal('2.0'))
In [4]: second_obj = TestModel.objects.create(amount=Decimal('2.0'), fixed_price=Decimal('3.00'))
In [5]: third_obj = TestModel.objects.create(amount=Decimal('2.0'), fixed_price=Decimal('3.00'), percentage=Decimal('10.0'))
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
...
ValueError: {'percentage': ValidationError([u'Please provide only one of: Fixed price, Percentage'])}
Conditionally require all fields
from django.db import models
from django.conf import settings
from extra_validator import FieldValidationMixin
class TestModel(FieldValidationMixin, models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL)
amount = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True)
fixed_price = models.DecimalField(max_digits=7, decimal_places=2, null=True, blank=True)
percentage = models.DecimalField(max_digits=3, decimal_places=0, null=True, blank=True)
CONDITIONAL_REQUIRED_FIELDS = [
(
lambda instance: instance.user.is_active, ['amount', 'percentage'],
),
]
Example
In [1]: from decimal import Decimal
in [2]: from django.contrib.auth import get_user_model
In [3]: from demo.models import TestModel
In [4]: user = get_user_model().objects.create(username='test', is_active=True)
In [5]: first_obj = TestModel.objects.create(user=user, amount=Decimal('2.0'))
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
...
ValueError: {u'percentage': ValidationError([u'Please provide a value for: "percentage"'])}
Conditionally require at least one field in a collection
from django.db import models
from django.conf import settings
from extra_validator import FieldValidationMixin
class TestModel(FieldValidationMixin, models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL)
amount = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True)
fixed_price = models.DecimalField(max_digits=7, decimal_places=2, null=True, blank=True)
percentage = models.DecimalField(max_digits=3, decimal_places=0, null=True, blank=True)
CONDITIONAL_REQUIRED_TOGGLE_FIELDS = [
(
lambda instance: instance.user.is_active, ['fixed_price', 'percentage', 'amount'],
),
]
Example
In [1]: from decimal import Decimal
in [2]: from django.contrib.auth import get_user_model
In [3]: from demo.models import TestModel
In [4]: user = get_user_model().objects.create(username='test', is_active=True)
In [5]: first_obj = TestModel.objects.create(user=user)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
...
ValueError: {'__all__': ValidationError([u'Please provide a valid value for any of the following fields: Fixed price, Percentage, Amount'])}
In [6]: second_obj = TestModel.objects.create(user=user, amount=Decimal('2'), fixed_price=Decimal('2'))
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
...
ValueError: {'__all__': ValidationError([u'Please provide only one of the following fields: Fixed price, Percentage, Amount'])}
Model Attributes
This is done using model attributes below.
REQUIRED_FIELDS = []
REQUIRED_TOGGLE_FIELDS = []
REQUIRED_MIN_FIELDS = []
OPTIONAL_TOGGLE_FIELDS = []
CONDITIONAL_REQUIRED_FIELDS = []
CONDITIONAL_REQUIRED_TOGGLE_FIELDS = []
CONDITIONAL_REQUIRED_MIN_FIELDS = []
CONDITIONAL_REQUIRED_EMPTY_FIELDS = []
License
django-extra-field-validation is distributed under the terms of both
at your option.
TODO's
- [ ] Support
CONDITIONAL_NON_REQUIRED_TOGGLE_FIELDS
- [ ] Support
CONDITIONAL_NON_REQUIRED_FIELDS
- [ ] Move to support class and function based validators that use the instance object this should enable cross field model validation.