Просмотр исходного кода

#8198: Rename CustomField.validation_unique to unique (#17325)

* #8198: Rename CustomField.validation_unique to unique

* Update CustomField model documentation
Jeremy Stretch 1 год назад
Родитель
Сommit
b4dd57f3c7

+ 5 - 5
docs/models/extras/customfield.md

@@ -57,7 +57,11 @@ A numeric weight used to override alphabetic ordering of fields by name. Custom
 
 ### Required
 
-If checked, this custom field must be populated with a valid value for the object to pass validation.
+If enabled, this custom field must be populated with a valid value for the object to pass validation.
+
+### Unique
+
+If enabled, each object must have a unique value set for this custom field (per object type).
 
 ### Description
 
@@ -116,7 +120,3 @@ For numeric custom fields only. The maximum valid value (optional).
 ### Validation Regex
 
 For string-based custom fields only. A regular expression used to validate the field's value (optional).
-
-### Uniqueness Validation
-
-If enabled, each object must have a unique value set for this custom field (per object type).

+ 4 - 3
netbox/extras/api/serializers_/customfields.py

@@ -61,9 +61,10 @@ class CustomFieldSerializer(ValidatedModelSerializer):
         model = CustomField
         fields = [
             'id', 'url', 'display_url', 'display', 'object_types', 'type', 'related_object_type', 'data_type',
-            'name', 'label', 'group_name', 'description', 'required', 'search_weight', 'filter_logic', 'ui_visible',
-            'ui_editable', 'is_cloneable', 'default', 'related_object_filter', 'weight', 'validation_minimum', 'validation_maximum',
-            'validation_regex', 'validation_unique', 'choice_set', 'comments', 'created', 'last_updated',
+            'name', 'label', 'group_name', 'description', 'required', 'unique', 'search_weight', 'filter_logic',
+            'ui_visible', 'ui_editable', 'is_cloneable', 'default', 'related_object_filter', 'weight',
+            'validation_minimum', 'validation_maximum', 'validation_regex', 'choice_set', 'comments', 'created',
+            'last_updated',
         ]
         brief_fields = ('id', 'url', 'display', 'name', 'description')
 

+ 2 - 2
netbox/extras/filtersets.py

@@ -158,9 +158,9 @@ class CustomFieldFilterSet(ChangeLoggedModelFilterSet):
     class Meta:
         model = CustomField
         fields = (
-            'id', 'name', 'label', 'group_name', 'required', 'search_weight', 'filter_logic', 'ui_visible',
+            'id', 'name', 'label', 'group_name', 'required', 'unique', 'search_weight', 'filter_logic', 'ui_visible',
             'ui_editable', 'weight', 'is_cloneable', 'description', 'validation_minimum', 'validation_maximum',
-            'validation_regex', 'validation_unique',
+            'validation_regex',
         )
 
     def search(self, queryset, name, value):

+ 7 - 9
netbox/extras/forms/bulk_edit.py

@@ -44,6 +44,11 @@ class CustomFieldBulkEditForm(BulkEditForm):
         required=False,
         widget=BulkEditNullBooleanSelect()
     )
+    unique = forms.NullBooleanField(
+        label=_('Must be unique'),
+        required=False,
+        widget=BulkEditNullBooleanSelect()
+    )
     weight = forms.IntegerField(
         label=_('Weight'),
         required=False
@@ -79,19 +84,12 @@ class CustomFieldBulkEditForm(BulkEditForm):
         label=_('Validation regex'),
         required=False
     )
-    validation_unique = forms.NullBooleanField(
-        label=_('Must be unique'),
-        required=False,
-        widget=BulkEditNullBooleanSelect()
-    )
     comments = CommentField()
 
     fieldsets = (
-        FieldSet('group_name', 'description', 'weight', 'choice_set', name=_('Attributes')),
+        FieldSet('group_name', 'description', 'weight', 'required', 'unique', 'choice_set', name=_('Attributes')),
         FieldSet('ui_visible', 'ui_editable', 'is_cloneable', name=_('Behavior')),
-        FieldSet(
-            'validation_minimum', 'validation_maximum', 'validation_regex', 'validation_unique', name=_('Validation')
-        ),
+        FieldSet('validation_minimum', 'validation_maximum', 'validation_regex', name=_('Validation')),
     )
     nullable_fields = ('group_name', 'description', 'choice_set')
 

+ 3 - 4
netbox/extras/forms/bulk_import.py

@@ -72,10 +72,9 @@ class CustomFieldImportForm(CSVModelForm):
     class Meta:
         model = CustomField
         fields = (
-            'name', 'label', 'group_name', 'type', 'object_types', 'related_object_type', 'required', 'description',
-            'search_weight', 'filter_logic', 'default', 'choice_set', 'weight', 'validation_minimum',
-            'validation_maximum', 'validation_regex', 'validation_unique', 'ui_visible', 'ui_editable', 'is_cloneable',
-            'comments',
+            'name', 'label', 'group_name', 'type', 'object_types', 'related_object_type', 'required', 'unique',
+            'description', 'search_weight', 'filter_logic', 'default', 'choice_set', 'weight', 'validation_minimum',
+            'validation_maximum', 'validation_regex', 'ui_visible', 'ui_editable', 'is_cloneable', 'comments',
         )
 
 

+ 11 - 12
netbox/extras/forms/filtersets.py

@@ -40,12 +40,11 @@ class CustomFieldFilterForm(SavedFiltersMixin, FilterForm):
     fieldsets = (
         FieldSet('q', 'filter_id'),
         FieldSet(
-            'type', 'related_object_type_id', 'group_name', 'weight', 'required', 'choice_set_id', 'ui_visible',
-            'ui_editable', 'is_cloneable', name=_('Attributes')
-        ),
-        FieldSet(
-            'validation_minimum', 'validation_maximum', 'validation_regex', 'validation_unique', name=_('Validation')
+            'type', 'related_object_type_id', 'group_name', 'weight', 'required', 'unique', 'choice_set_id',
+            name=_('Attributes')
         ),
+        FieldSet('ui_visible', 'ui_editable', 'is_cloneable', name=_('Behavior')),
+        FieldSet('validation_minimum', 'validation_maximum', 'validation_regex', name=_('Validation')),
     )
     related_object_type_id = ContentTypeMultipleChoiceField(
         queryset=ObjectType.objects.with_feature('custom_fields'),
@@ -72,6 +71,13 @@ class CustomFieldFilterForm(SavedFiltersMixin, FilterForm):
             choices=BOOLEAN_WITH_BLANK_CHOICES
         )
     )
+    unique = forms.NullBooleanField(
+        label=_('Must be unique'),
+        required=False,
+        widget=forms.Select(
+            choices=BOOLEAN_WITH_BLANK_CHOICES
+        )
+    )
     choice_set_id = DynamicModelMultipleChoiceField(
         queryset=CustomFieldChoiceSet.objects.all(),
         required=False,
@@ -106,13 +112,6 @@ class CustomFieldFilterForm(SavedFiltersMixin, FilterForm):
         label=_('Validation regex'),
         required=False
     )
-    validation_unique = forms.NullBooleanField(
-        label=_('Must be unique'),
-        required=False,
-        widget=forms.Select(
-            choices=BOOLEAN_WITH_BLANK_CHOICES
-        )
-    )
 
 
 class CustomFieldChoiceSetFilterForm(SavedFiltersMixin, FilterForm):

+ 2 - 2
netbox/extras/forms/model_forms.py

@@ -69,8 +69,8 @@ class CustomFieldForm(forms.ModelForm):
 
     fieldsets = (
         FieldSet(
-            'object_types', 'name', 'label', 'group_name', 'description', 'type', 'required', 'validation_unique',
-            'default', name=_('Custom Field')
+            'object_types', 'name', 'label', 'group_name', 'description', 'type', 'required', 'unique', 'default',
+            name=_('Custom Field')
         ),
         FieldSet(
             'search_weight', 'filter_logic', 'ui_visible', 'ui_editable', 'weight', 'is_cloneable', name=_('Behavior')

+ 1 - 1
netbox/extras/migrations/0118_customfield_uniqueness.py

@@ -10,7 +10,7 @@ class Migration(migrations.Migration):
     operations = [
         migrations.AddField(
             model_name='customfield',
-            name='validation_unique',
+            name='unique',
             field=models.BooleanField(default=False),
         ),
     ]

+ 11 - 11
netbox/extras/models/customfields.py

@@ -129,7 +129,12 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
     required = models.BooleanField(
         verbose_name=_('required'),
         default=False,
-        help_text=_("If true, this field is required when creating new objects or editing an existing object.")
+        help_text=_("This field is required when creating new objects or editing an existing object.")
+    )
+    unique = models.BooleanField(
+        verbose_name=_('must be unique'),
+        default=False,
+        help_text=_("The value of this field must be unique for the assigned object")
     )
     search_weight = models.PositiveSmallIntegerField(
         verbose_name=_('search weight'),
@@ -189,11 +194,6 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
             'example, <code>^[A-Z]{3}$</code> will limit values to exactly three uppercase letters.'
         )
     )
-    validation_unique = models.BooleanField(
-        verbose_name=_('must be unique'),
-        default=False,
-        help_text=_('The value of this field must be unique for the assigned object')
-    )
     choice_set = models.ForeignKey(
         to='CustomFieldChoiceSet',
         on_delete=models.PROTECT,
@@ -229,9 +229,9 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
     objects = CustomFieldManager()
 
     clone_fields = (
-        'object_types', 'type', 'related_object_type', 'group_name', 'description', 'required', 'search_weight',
-        'filter_logic', 'default', 'weight', 'validation_minimum', 'validation_maximum', 'validation_regex',
-        'validation_unique', 'choice_set', 'ui_visible', 'ui_editable', 'is_cloneable',
+        'object_types', 'type', 'related_object_type', 'group_name', 'description', 'required', 'unique',
+        'search_weight', 'filter_logic', 'default', 'weight', 'validation_minimum', 'validation_maximum',
+        'validation_regex', 'choice_set', 'ui_visible', 'ui_editable', 'is_cloneable',
     )
 
     class Meta:
@@ -349,9 +349,9 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
             })
 
         # Uniqueness can not be enforced for boolean fields
-        if self.validation_unique and self.type == CustomFieldTypeChoices.TYPE_BOOLEAN:
+        if self.unique and self.type == CustomFieldTypeChoices.TYPE_BOOLEAN:
             raise ValidationError({
-                'validation_unique': _("Uniqueness cannot be enforced for boolean fields")
+                'unique': _("Uniqueness cannot be enforced for boolean fields")
             })
 
         # Choice set must be set on selection fields, and *only* on selection fields

+ 10 - 7
netbox/extras/tables/tables.py

@@ -65,6 +65,10 @@ class CustomFieldTable(NetBoxTable):
         verbose_name=_('Required'),
         false_mark=None
     )
+    unique = columns.BooleanColumn(
+        verbose_name=_('Validate Uniqueness'),
+        false_mark=None
+    )
     ui_visible = columns.ChoiceFieldColumn(
         verbose_name=_('Visible')
     )
@@ -99,19 +103,18 @@ class CustomFieldTable(NetBoxTable):
     validation_regex = tables.Column(
         verbose_name=_('Validation Regex'),
     )
-    validation_unique = columns.BooleanColumn(
-        verbose_name=_('Validate Uniqueness'),
-    )
 
     class Meta(NetBoxTable.Meta):
         model = CustomField
         fields = (
             'pk', 'id', 'name', 'object_types', 'label', 'type', 'related_object_type', 'group_name', 'required',
-            'default', 'description', 'search_weight', 'filter_logic', 'ui_visible', 'ui_editable', 'is_cloneable',
-            'weight', 'choice_set', 'choices', 'validation_minimum', 'validation_maximum', 'validation_regex',
-            'validation_unique', 'comments', 'created', 'last_updated',
+            'unique', 'default', 'description', 'search_weight', 'filter_logic', 'ui_visible', 'ui_editable',
+            'is_cloneable', 'weight', 'choice_set', 'choices', 'validation_minimum', 'validation_maximum',
+            'validation_regex', 'comments', 'created', 'last_updated',
+        )
+        default_columns = (
+            'pk', 'name', 'object_types', 'label', 'group_name', 'type', 'required', 'unique', 'description',
         )
-        default_columns = ('pk', 'name', 'object_types', 'label', 'group_name', 'type', 'required', 'description')
 
 
 class CustomFieldChoiceSetTable(NetBoxTable):

+ 1 - 1
netbox/extras/tests/test_customfields.py

@@ -1143,7 +1143,7 @@ class CustomFieldAPITest(APITestCase):
     def test_uniqueness_validation(self):
         # Create a unique custom field
         cf_text = CustomField.objects.get(name='text_field')
-        cf_text.validation_unique = True
+        cf_text.unique = True
         cf_text.save()
 
         # Set a value on site 1

+ 1 - 1
netbox/netbox/models/features.py

@@ -288,7 +288,7 @@ class CustomFieldsMixin(models.Model):
                 ))
 
             # Validate uniqueness if enforced
-            if custom_fields[field_name].validation_unique and value not in CUSTOMFIELD_EMPTY_VALUES:
+            if custom_fields[field_name].unique and value not in CUSTOMFIELD_EMPTY_VALUES:
                 if self._meta.model.objects.exclude(pk=self.pk).filter(**{
                     f'custom_field_data__{field_name}': value
                 }).exists():

+ 4 - 4
netbox/templates/extras/customfield.html

@@ -38,6 +38,10 @@
           <th scope="row">{% trans "Required" %}</th>
           <td>{% checkmark object.required %}</td>
         </tr>
+        <tr>
+          <th scope="row">{% trans "Must be Unique" %}</th>
+          <td>{% checkmark object.unique %}</td>
+        </tr>
         <tr>
           <th scope="row">{% trans "Cloneable" %}</th>
           <td>{% checkmark object.is_cloneable %}</td>
@@ -128,10 +132,6 @@
             {% endif %}
           </td>
         </tr>
-        <tr>
-          <th scope="row">{% trans "Must be Unique" %}</th>
-          <td>{% checkmark object.validation_unique %}</td>
-        </tr>
       </table>
     </div>
     <div class="card">