django-dynamic-model-validation
Introduction
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 table check constraints if needed but currently validation will only be handled at the model level.
Installation
django-dynamic-model-validation is distributed on PyPI as a universal wheel and is available on Linux/macOS and Windows and supports Python 2.7/3.5+ and PyPy.
pip install django-dynamic-model-validation
Usage
This provides model level validation which includes:
- Conditional field validation
- Cross field validation
- Required field validation
- Optional field validation
Require a single field in a collection
from django.db import models
from dynamic_validator import ModelFieldRequiredMixin
class TestModel(ModelFieldRequiredMixin, 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'], # Require only one of the following fields.
]
python manage.py shell
...
>>> from decimal import Decimal
>>> from demo.models import TestModel
>>> TestModel.objects.create(amount=Decimal('2.50'), fixed_price=Decimal('3.00'))
...
ValueError: {'fixed_price': ValidationError([u'Please provide only one of: Amount, Fixed price, Percentage'])}
Require all fields
from django.db import models
from dynamic_validator import ModelFieldRequiredMixin
class TestModel(ModelFieldRequiredMixin, 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'] # Always requires an amount to create the instance.
python manage.py shell
...
>>> from decimal import Decimal
>>> from demo.models import TestModel
>>> TestModel.objects.create(fixed_price=Decimal('3.00'))
...
ValueError: {'amount': ValidationError([u'Please provide a value for: "amount".'])}
Optionally required fields
from django.db import models
from dynamic_validator import ModelFieldRequiredMixin
class TestModel(ModelFieldRequiredMixin, 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'] # Optionally validates that only fixed price/percentage are provided when present.
]
python manage.py shell
...
>>> from decimal import Decimal
>>> from demo.models import TestModel
>>> first_obj = TestModel.objects.create(amount=Decimal('2.0'))
>>> second_obj = TestModel.objects.create(amount=Decimal('2.0'), fixed_price=Decimal('3.00'))
>>> third_obj = TestModel.objects.create(amount=Decimal('2.0'), fixed_price=Decimal('3.00'), percentage=Decimal('10.0'))
...
ValueError: {'percentage': ValidationError([u'Please provide only one of: Fixed price, Percentage'])}
Conditional required fields
from django.db import models
from django.conf import settings
from dynamic_validator import ModelFieldRequiredMixin
class TestModel(ModelFieldRequiredMixin, 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'],
),
]
python manage.py shell
...
>>> from decimal import Decimal
>>> from django.contrib.auth import get_user_model
>>> from demo.models import TestModel
>>> user = get_user_model().objects.create(username='test', is_active=True)
>>> first_obj = TestModel.objects.create(user=user, amount=Decimal('2.0'))
...
ValueError: {u'percentage': ValidationError([u'Please provide a value for: "percentage"'])}
Conditional required optional fields
from django.db import models
from django.conf import settings
from dynamic_validator import ModelFieldRequiredMixin
class TestModel(ModelFieldRequiredMixin, 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'],
),
]
python manage.py shell
...
>>> from decimal import Decimal
>>> from django.contrib.auth import get_user_model
>>> from demo.models import TestModel
>>> user = get_user_model().objects.create(username='test', is_active=True)
>>> first_obj = TestModel.objects.create(user=user)
...
ValueError: {'__all__': ValidationError([u'Please provide a valid value for any of the following fields: Fixed price, Percentage, Amount'])}
...
>>>first_obj = TestModel.objects.create(user=user, amount=Decimal('2'), fixed_price=Decimal('2'))
...
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.
# Using a list/iterable: [['a', 'b'], ['c', 'd']] which validates that a field from each item is provided.
REQUIRED_TOGGLE_FIELDS = []
# Using a list/iterable validates that all fields are provided.
REQUIRED_FIELDS = []
# Optional toggle fields list: [['a', 'b']] which runs the validation only when any of the fields are present.
OPTIONAL_TOGGLE_FIELDS = []
# Conditional field validation using a list of tuples the condition which could be boolean or a callable and the list/iterable of fields that are required if the condition evaluates to `True`.
# [(condition, [fields]), (condition, fields)]
# Using a callable CONDITIONAL_REQUIRED_FIELDS = [(lambda instance: instance.is_admin, ['a', 'd'])]
# Using a boolean CONDITIONAL_REQUIRED_TOGGLE_FIELDS = [(True, ['b', 'c']), (True, ['d', f])]
# asserts that either 'b' or 'c' is provided and either 'd' or 'f'.
# (Note: This can also be handled using REQUIRED_FIELDS/REQUIRED_TOGGLE_FIELDS)
# Validates that all fields are present if the condition is True
CONDITIONAL_REQUIRED_FIELDS = []
# Validated at least one not both fields are provided if the condition is True.
CONDITIONAL_REQUIRED_TOGGLE_FIELDS = []
License
django-dynamic-model-validation is distributed under the terms of both
at your option.
TODO's
- Move to support class and function based validators that use the instance object this should enable cross field model validation.
Metadata
Release files for django-dynamic-model-validation 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| django-dynamic-model-validation-1.0.0.tar.gz | 11.4 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| django_dynamic_model_validation-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 27.1 kB
Release files / django-dynamic-model-validation-1.0.0.tar.gz
| Download URL | django-dynamic-model-validation-1.0.0.tar.gz |
|---|---|
| Size | 11.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
30612c12ef2c6001b78ea154be8754b5729b832e9279a8ebf307a9ff3def1bd5
|
|
BLAKE2b-256 checksum How to use checksums |
b8c0340a05de9b01d93c95d11b27782b952ea23c4cba9f28318ba4314c17834f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/3.3.0 pkginfo/1.5.0.1 requests/2.25.1 setuptools/53.1.0 requests-toolbelt/0.8.0 tqdm/4.30.0 CPython/3.6.4
|
Release files / django_dynamic_model_validation-1.0.0-py3-none-any.whl
| Download URL | django_dynamic_model_validation-1.0.0-py3-none-any.whl |
|---|---|
| Size | 15.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
39719897cc73b92dc052bcfc42506ad946fd6b5d358b5892a8f7de2c6c401e1e
|
|
BLAKE2b-256 checksum How to use checksums |
7d3d19772333cd61b563305b91c74b2402c61e524d5d8a305a9b9c49a13dcd9c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/3.3.0 pkginfo/1.5.0.1 requests/2.25.1 setuptools/53.1.0 requests-toolbelt/0.8.0 tqdm/4.30.0 CPython/3.6.4
|
Security for django-dynamic-model-validation 1.0.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.