Package: django-extra-field-validation
django-extra-field-validationTable of ContentsBackgroundInstallationUsageRequire all fieldsRequire at least one field in a collectionOptionally require at least one field in a collectionConditionally require all fieldsConditionally require at least one field in a collectionModel AttributesLicenseTODO'sBackgroundThis 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.Installationpipinstalldjango-extra-field-validationUsageRequire all fieldsfromdjango.dbimportmodelsfromextra_validatorimportFieldValidationMixinclassTestModel(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']# Always requires an amount to create the instance.ExampleIn[1]:fromdecimalimportDecimalIn[2]:fromdemo.modelsimportTestModelIn[3]:TestModel.objects.create(fixed_price=Decimal('3.00'))---------------------------------------------------------------------------ValueErrorTraceback(mostrecentcalllast)...ValueError:{'amount':ValidationError([u'Please provide a value for: "amount".'])}Require at least one field in a collectionfromdjango.dbimportmodelsfromextra_validatorimportFieldValidationMixinclassTestModel(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'],# Require only one of the following fields.]ExampleIn[1]:fromdecimalimportDecimalIn[2]:fromdemo.modelsimportTestModelIn[3]:TestModel.objects.create(amount=Decimal('2.50'),fixed_price=Decimal('3.00'))---------------------------------------------------------------------------ValueErrorTraceback(mostrecentcalllast)...ValueError:{'fixed_price':ValidationError([u'Please provide only one of: Amount, Fixed price, Percentage'])}Optionally require at least one field in a collectionfromdjango.dbimportmodelsfromextra_validatorimportFieldValidationMixinclassTestModel(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']# Optionally validates that only fixed price/percentage are provided when present.]ExampleIn[1]:fromdecimalimportDecimalIn[2]:fromdemo.modelsimportTestModelIn[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'))---------------------------------------------------------------------------ValueErrorTraceback(mostrecentcalllast)...ValueError:{'percentage':ValidationError([u'Please provide only one of: Fixed price, Percentage'])}Conditionally require all fieldsfromdjango.dbimportmodelsfromdjango.confimportsettingsfromextra_validatorimportFieldValidationMixinclassTestModel(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=[(lambdainstance:instance.user.is_active,['amount','percentage'],),]ExampleIn[1]:fromdecimalimportDecimalin[2]:fromdjango.contrib.authimportget_user_modelIn[3]:fromdemo.modelsimportTestModelIn[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'))---------------------------------------------------------------------------ValueErrorTraceback(mostrecentcalllast)...ValueError:{u'percentage':ValidationError([u'Please provide a value for: "percentage"'])}Conditionally require at least one field in a collectionfromdjango.dbimportmodelsfromdjango.confimportsettingsfromextra_validatorimportFieldValidationMixinclassTestModel(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=[(lambdainstance:instance.user.is_active,['fixed_price','percentage','amount'],),]ExampleIn[1]:fromdecimalimportDecimalin[2]:fromdjango.contrib.authimportget_user_modelIn[3]:fromdemo.modelsimportTestModelIn[4]:user=get_user_model().objects.create(username='test',is_active=True)In[5]:first_obj=TestModel.objects.create(user=user)---------------------------------------------------------------------------ValueErrorTraceback(mostrecentcalllast)...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'))---------------------------------------------------------------------------ValueErrorTraceback(mostrecentcalllast)...ValueError:{'__all__':ValidationError([u'Please provide only one of the following fields: Fixed price, Percentage, Amount'])}Model AttributesThis is done using model attributes below.# A list of required fieldsREQUIRED_FIELDS=[]#  A list of fields with at most one required.REQUIRED_TOGGLE_FIELDS=[]# A list of field with at least one required.REQUIRED_MIN_FIELDS=[]# Optional list of fields with at most one required.OPTIONAL_TOGGLE_FIELDS=[]# Conditional field required list of tuples the condition a boolean or a callable.# [(lambda user: user.is_admin, ['first_name', 'last_name'])] : Both 'first_name' or 'last_name'# If condition is True ensure that all fields are setCONDITIONAL_REQUIRED_FIELDS=[]# [(lambda user: user.is_admin, ['first_name', 'last_name'])] : Either 'first_name' or 'last_name'# If condition is True ensure that at most one field is setCONDITIONAL_REQUIRED_TOGGLE_FIELDS=[]# [(lambda user: user.is_admin, ['first_name', 'last_name'])] : At least 'first_name' or 'last_name' provided or both# If condition is True ensure that at least one field is setCONDITIONAL_REQUIRED_MIN_FIELDS=[]# [(lambda user: user.is_admin, ['first_name', 'last_name'])] : Both 'first_name' and 'last_name' isn't provided# If condition is True ensure none of the fields are providedCONDITIONAL_REQUIRED_EMPTY_FIELDS=[]Licensedjango-extra-field-validation is distributed under the terms of bothMIT LicenseApache License, Version 2.0at your option.TODO's[ ] SupportCONDITIONAL_NON_REQUIRED_TOGGLE_FIELDS[ ] SupportCONDITIONAL_NON_REQUIRED_FIELDS[ ] Move to support class and function based validators that use the instance object this should enable cross field model validation.