Răsfoiți Sursa

Cleanup & docs

jeremystretch 2 ani în urmă
părinte
comite
5cd3ad0b12

+ 4 - 0
docs/models/extras/customfield.md

@@ -82,6 +82,10 @@ The default value to populate for the custom field when creating new objects (op
 
 
 For choice and multi-choice custom fields only. A comma-delimited list of the available choices.
 For choice and multi-choice custom fields only. A comma-delimited list of the available choices.
 
 
+### Cloneable
+
+If enabled, values from this field will be automatically pre-populated when cloning existing objects.
+
 ### Minimum Value
 ### Minimum Value
 
 
 For numeric custom fields only. The minimum valid value (optional).
 For numeric custom fields only. The minimum valid value (optional).

+ 1 - 0
docs/release-notes/version-3.5.md

@@ -28,6 +28,7 @@ A new ASN range model has been introduced to facilitate the provisioning of new
 
 
 * [#7947](https://github.com/netbox-community/netbox/issues/7947) - Enable marking IP ranges as fully utilized
 * [#7947](https://github.com/netbox-community/netbox/issues/7947) - Enable marking IP ranges as fully utilized
 * [#8272](https://github.com/netbox-community/netbox/issues/8272) - Support bridge relationships among device type interfaces
 * [#8272](https://github.com/netbox-community/netbox/issues/8272) - Support bridge relationships among device type interfaces
+* [#8749](https://github.com/netbox-community/netbox/issues/8749) - Support replicating custom field values when cloning an object
 * [#8958](https://github.com/netbox-community/netbox/issues/8958) - Changes in background job status can trigger webhooks
 * [#8958](https://github.com/netbox-community/netbox/issues/8958) - Changes in background job status can trigger webhooks
 * [#9073](https://github.com/netbox-community/netbox/issues/9073) - Enable syncing config context data from remote sources
 * [#9073](https://github.com/netbox-community/netbox/issues/9073) - Enable syncing config context data from remote sources
 * [#9653](https://github.com/netbox-community/netbox/issues/9653) - Enable setting a default platform for device types
 * [#9653](https://github.com/netbox-community/netbox/issues/9653) - Enable setting a default platform for device types

+ 3 - 2
netbox/extras/api/serializers.py

@@ -98,8 +98,9 @@ class CustomFieldSerializer(ValidatedModelSerializer):
         model = CustomField
         model = CustomField
         fields = [
         fields = [
             'id', 'url', 'display', 'content_types', 'type', 'object_type', 'data_type', 'name', 'label', 'group_name',
             'id', 'url', 'display', 'content_types', 'type', 'object_type', 'data_type', 'name', 'label', 'group_name',
-            'description', 'required', 'search_weight', 'filter_logic', 'ui_visibility', 'is_cloneable', 'default', 'weight',
-            'validation_minimum', 'validation_maximum', 'validation_regex', 'choices', 'created', 'last_updated',
+            'description', 'required', 'search_weight', 'filter_logic', 'ui_visibility', 'is_cloneable', 'default',
+            'weight', 'validation_minimum', 'validation_maximum', 'validation_regex', 'choices', 'created',
+            'last_updated',
         ]
         ]
 
 
     def get_data_type(self, obj):
     def get_data_type(self, obj):

+ 1 - 1
netbox/extras/filtersets.py

@@ -78,7 +78,7 @@ class CustomFieldFilterSet(BaseFilterSet):
         model = CustomField
         model = CustomField
         fields = [
         fields = [
             'id', 'content_types', 'name', 'group_name', 'required', 'search_weight', 'filter_logic', 'ui_visibility',
             'id', 'content_types', 'name', 'group_name', 'required', 'search_weight', 'filter_logic', 'ui_visibility',
-            'weight', 'description',
+            'weight', 'is_cloneable', 'description',
         ]
         ]
 
 
     def search(self, queryset, name, value):
     def search(self, queryset, name, value):

+ 9 - 1
netbox/extras/forms/filtersets.py

@@ -37,7 +37,9 @@ __all__ = (
 class CustomFieldFilterForm(SavedFiltersMixin, FilterForm):
 class CustomFieldFilterForm(SavedFiltersMixin, FilterForm):
     fieldsets = (
     fieldsets = (
         (None, ('q', 'filter_id')),
         (None, ('q', 'filter_id')),
-        ('Attributes', ('type', 'content_type_id', 'group_name', 'weight', 'required', 'ui_visibility')),
+        ('Attributes', (
+            'type', 'content_type_id', 'group_name', 'weight', 'required', 'ui_visibility', 'is_cloneable',
+        )),
     )
     )
     content_type_id = ContentTypeMultipleChoiceField(
     content_type_id = ContentTypeMultipleChoiceField(
         queryset=ContentType.objects.filter(FeatureQuery('custom_fields').get_query()),
         queryset=ContentType.objects.filter(FeatureQuery('custom_fields').get_query()),
@@ -66,6 +68,12 @@ class CustomFieldFilterForm(SavedFiltersMixin, FilterForm):
         required=False,
         required=False,
         label=_('UI visibility')
         label=_('UI visibility')
     )
     )
+    is_cloneable = forms.NullBooleanField(
+        required=False,
+        widget=forms.Select(
+            choices=BOOLEAN_WITH_BLANK_CHOICES
+        )
+    )
 
 
 
 
 class JobResultFilterForm(SavedFiltersMixin, FilterForm):
 class JobResultFilterForm(SavedFiltersMixin, FilterForm):

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

@@ -47,7 +47,7 @@ class CustomFieldForm(BootstrapMixin, forms.ModelForm):
         ('Custom Field', (
         ('Custom Field', (
             'content_types', 'name', 'label', 'group_name', 'type', 'object_type', 'required', 'description',
             'content_types', 'name', 'label', 'group_name', 'type', 'object_type', 'required', 'description',
         )),
         )),
-        ('Behavior', ('search_weight', 'filter_logic', 'ui_visibility', 'is_cloneable', 'weight')),
+        ('Behavior', ('search_weight', 'filter_logic', 'ui_visibility', 'weight', 'is_cloneable')),
         ('Values', ('default', 'choices')),
         ('Values', ('default', 'choices')),
         ('Validation', ('validation_minimum', 'validation_maximum', 'validation_regex')),
         ('Validation', ('validation_minimum', 'validation_maximum', 'validation_regex')),
     )
     )

+ 1 - 1
netbox/extras/migrations/0085_customfield_is_cloneable.py → netbox/extras/migrations/0089_customfield_is_cloneable.py

@@ -6,7 +6,7 @@ from django.db import migrations, models
 class Migration(migrations.Migration):
 class Migration(migrations.Migration):
 
 
     dependencies = [
     dependencies = [
-        ('extras', '0084_staging'),
+        ('extras', '0088_jobresult_webhooks'),
     ]
     ]
 
 
     operations = [
     operations = [

+ 3 - 1
netbox/extras/tables/tables.py

@@ -29,12 +29,14 @@ class CustomFieldTable(NetBoxTable):
     content_types = columns.ContentTypesColumn()
     content_types = columns.ContentTypesColumn()
     required = columns.BooleanColumn()
     required = columns.BooleanColumn()
     ui_visibility = columns.ChoiceFieldColumn(verbose_name="UI visibility")
     ui_visibility = columns.ChoiceFieldColumn(verbose_name="UI visibility")
+    is_cloneable = columns.BooleanColumn()
 
 
     class Meta(NetBoxTable.Meta):
     class Meta(NetBoxTable.Meta):
         model = CustomField
         model = CustomField
         fields = (
         fields = (
             'pk', 'id', 'name', 'content_types', 'label', 'type', 'group_name', 'required', 'default', 'description',
             'pk', 'id', 'name', 'content_types', 'label', 'type', 'group_name', 'required', 'default', 'description',
-            'search_weight', 'filter_logic', 'ui_visibility', 'is_cloneable', 'weight', 'choices', 'created', 'last_updated',
+            'search_weight', 'filter_logic', 'ui_visibility', 'is_cloneable', 'weight', 'choices', 'created',
+            'last_updated',
         )
         )
         default_columns = ('pk', 'name', 'content_types', 'label', 'group_name', 'type', 'required', 'description')
         default_columns = ('pk', 'name', 'content_types', 'label', 'group_name', 'type', 'required', 'description')
 
 

+ 3 - 6
netbox/netbox/models/features.py

@@ -121,14 +121,11 @@ class CloningMixin(models.Model):
         if is_taggable(self):
         if is_taggable(self):
             attrs['tags'] = [tag.pk for tag in self.tags.all()]
             attrs['tags'] = [tag.pk for tag in self.tags.all()]
 
 
-        # check custom fields
+        # Include any cloneable custom fields
         if hasattr(self, 'custom_field_data'):
         if hasattr(self, 'custom_field_data'):
-            from extras.models import CustomField
-
-            for field in CustomField.objects.get_for_model(self):
+            for field in self.get_custom_fields():
                 if field.is_cloneable:
                 if field.is_cloneable:
-                    value = self.custom_field_data.get(field.name)
-                    attrs[f'cf_{field.name}'] = field.deserialize(value)
+                    attrs[f'cf_{field.name}'] = self.custom_field_data.get(field.name)
 
 
         return attrs
         return attrs