Skip to content

Commit a850a9c

Browse files
authored
Merge pull request #170 from netboxlabs/npl-389-unique-3
NPL-389 fix unique check
2 parents 636d67d + 91335ba commit a850a9c

File tree

1 file changed

+45
-1
lines changed

1 file changed

+45
-1
lines changed

netbox_custom_objects/models.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
# from django.contrib.contenttypes.management import create_contenttypes
1313
from django.contrib.contenttypes.models import ContentType
1414
from django.core.validators import RegexValidator, ValidationError
15-
from django.db import connection, models
15+
from django.db import connection, IntegrityError, models, transaction
1616
from django.db.models import Q
1717
from django.db.models.functions import Lower
1818
from django.db.models.signals import pre_delete
@@ -52,6 +52,13 @@
5252
from netbox_custom_objects.constants import APP_LABEL, RESERVED_FIELD_NAMES
5353
from netbox_custom_objects.field_types import FIELD_TYPE_CLASS
5454

55+
56+
class UniquenessConstraintTestError(Exception):
57+
"""Custom exception used to signal successful uniqueness constraint test."""
58+
59+
pass
60+
61+
5562
USER_TABLE_DATABASE_NAME_PREFIX = "custom_objects_"
5663

5764

@@ -872,6 +879,43 @@ def clean(self):
872879
{"unique": _("Uniqueness cannot be enforced for boolean fields")}
873880
)
874881

882+
# Check if uniqueness constraint can be applied when changing from non-unique to unique
883+
if (
884+
self.pk
885+
and self.unique
886+
and not self.original.unique
887+
and not self._state.adding
888+
):
889+
field_type = FIELD_TYPE_CLASS[self.type]()
890+
model_field = field_type.get_model_field(self)
891+
model = self.custom_object_type.get_model()
892+
model_field.contribute_to_class(model, self.name)
893+
894+
old_field = field_type.get_model_field(self.original)
895+
old_field.contribute_to_class(model, self._original_name)
896+
897+
try:
898+
with transaction.atomic():
899+
with connection.schema_editor() as test_schema_editor:
900+
test_schema_editor.alter_field(model, old_field, model_field)
901+
# If we get here, the constraint was applied successfully
902+
# Now raise a custom exception to rollback the test transaction
903+
raise UniquenessConstraintTestError()
904+
except UniquenessConstraintTestError:
905+
# The constraint can be applied, validation passes
906+
pass
907+
except IntegrityError:
908+
# The constraint cannot be applied due to existing non-unique values
909+
raise ValidationError(
910+
{
911+
"unique": _(
912+
"Custom objects with non-unique values already exist so this action isn't permitted"
913+
)
914+
}
915+
)
916+
finally:
917+
self.custom_object_type.clear_model_cache(self.custom_object_type.id)
918+
875919
# Choice set must be set on selection fields, and *only* on selection fields
876920
if self.type in (
877921
CustomFieldTypeChoices.TYPE_SELECT,

0 commit comments

Comments
 (0)