mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Closes #7619: Permit custom validation rules to be defined as plain data or dotted path to class
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import importlib
|
||||
import logging
|
||||
|
||||
from django.conf import settings
|
||||
@@ -6,6 +7,7 @@ from django.db.models.signals import m2m_changed, post_save, pre_delete
|
||||
from django.dispatch import receiver, Signal
|
||||
from django_prometheus.models import model_deletes, model_inserts, model_updates
|
||||
|
||||
from extras.validators import CustomValidator
|
||||
from netbox.signals import post_clean
|
||||
from .choices import ObjectChangeActionChoices
|
||||
from .models import ConfigRevision, CustomField, ObjectChange
|
||||
@@ -159,7 +161,18 @@ m2m_changed.connect(handle_cf_removed_obj_types, sender=CustomField.content_type
|
||||
def run_custom_validators(sender, instance, **kwargs):
|
||||
model_name = f'{sender._meta.app_label}.{sender._meta.model_name}'
|
||||
validators = settings.CUSTOM_VALIDATORS.get(model_name, [])
|
||||
|
||||
for validator in validators:
|
||||
|
||||
# Loading a validator class by dotted path
|
||||
if type(validator) is str:
|
||||
module, cls = validator.rsplit('.', 1)
|
||||
validator = getattr(importlib.import_module(module), cls)()
|
||||
|
||||
# Constructing a new instance on the fly from a ruleset
|
||||
elif type(validator) is dict:
|
||||
validator = CustomValidator(validator)
|
||||
|
||||
validator(instance)
|
||||
|
||||
|
||||
|
@@ -119,3 +119,38 @@ class CustomValidatorTest(TestCase):
|
||||
@override_settings(CUSTOM_VALIDATORS={'dcim.site': [custom_validator]})
|
||||
def test_custom_valid(self):
|
||||
Site(name='foo', slug='foo').clean()
|
||||
|
||||
|
||||
class CustomValidatorConfigTest(TestCase):
|
||||
|
||||
@override_settings(
|
||||
CUSTOM_VALIDATORS={
|
||||
'dcim.site': [
|
||||
{'name': {'min_length': 5}}
|
||||
]
|
||||
}
|
||||
)
|
||||
def test_plain_data(self):
|
||||
"""
|
||||
Test custom validator configuration using plain data (as opposed to a CustomValidator
|
||||
class)
|
||||
"""
|
||||
with self.assertRaises(ValidationError):
|
||||
Site(name='abcd', slug='abcd').clean()
|
||||
Site(name='abcde', slug='abcde').clean()
|
||||
|
||||
@override_settings(
|
||||
CUSTOM_VALIDATORS={
|
||||
'dcim.site': (
|
||||
'extras.tests.test_customvalidator.MyValidator',
|
||||
)
|
||||
}
|
||||
)
|
||||
def test_dotted_path(self):
|
||||
"""
|
||||
Test custom validator configuration using a dotted path (string) reference to a
|
||||
CustomValidator class.
|
||||
"""
|
||||
Site(name='foo', slug='foo').clean()
|
||||
with self.assertRaises(ValidationError):
|
||||
Site(name='bar', slug='bar').clean()
|
||||
|
Reference in New Issue
Block a user