Browse Source

Closes #15357: Rename CustomField.object_type to related_object_type (#15366)

Jeremy Stretch 1 year ago
parent
commit
78dd65219f

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

@@ -38,7 +38,7 @@ The type of data this field holds. This must be one of the following:
 | Object             | A single NetBox object of the type defined by `object_type`        |
 | Multiple object    | One or more NetBox objects of the type defined by `object_type`    |
 
-### Object Type
+### Related Object Type
 
 For object and multiple-object fields only. Designates the type of NetBox object being referenced.
 

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

@@ -57,10 +57,10 @@ class CustomFieldsDataField(Field):
         for cf in self._get_custom_fields():
             value = cf.deserialize(obj.get(cf.name))
             if value is not None and cf.type == CustomFieldTypeChoices.TYPE_OBJECT:
-                serializer = get_serializer_for_model(cf.object_type.model_class())
+                serializer = get_serializer_for_model(cf.related_object_type.model_class())
                 value = serializer(value, nested=True, context=self.parent.context).data
             elif value is not None and cf.type == CustomFieldTypeChoices.TYPE_MULTIOBJECT:
-                serializer = get_serializer_for_model(cf.object_type.model_class())
+                serializer = get_serializer_for_model(cf.related_object_type.model_class())
                 value = serializer(value, nested=True, many=True, context=self.parent.context).data
             data[cf.name] = value
 
@@ -79,7 +79,7 @@ class CustomFieldsDataField(Field):
                     CustomFieldTypeChoices.TYPE_OBJECT,
                     CustomFieldTypeChoices.TYPE_MULTIOBJECT
             ):
-                serializer_class = get_serializer_for_model(cf.object_type.model_class())
+                serializer_class = get_serializer_for_model(cf.related_object_type.model_class())
                 many = cf.type == CustomFieldTypeChoices.TYPE_MULTIOBJECT
                 serializer = serializer_class(data=data[cf.name], nested=True, many=many, context=self.parent.context)
                 if serializer.is_valid():

+ 5 - 5
netbox/extras/api/serializers_/customfields.py

@@ -44,7 +44,7 @@ class CustomFieldSerializer(ValidatedModelSerializer):
         many=True
     )
     type = ChoiceField(choices=CustomFieldTypeChoices)
-    object_type = ContentTypeField(
+    related_object_type = ContentTypeField(
         queryset=ObjectType.objects.all(),
         required=False,
         allow_null=True
@@ -62,10 +62,10 @@ class CustomFieldSerializer(ValidatedModelSerializer):
     class Meta:
         model = CustomField
         fields = [
-            'id', 'url', 'display', 'object_types', 'type', 'object_type', 'data_type', 'name', 'label', 'group_name',
-            'description', 'required', 'search_weight', 'filter_logic', 'ui_visible', 'ui_editable', 'is_cloneable',
-            'default', 'weight', 'validation_minimum', 'validation_maximum', 'validation_regex', 'choice_set',
-            'created', 'last_updated',
+            'id', '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', 'weight', 'validation_minimum', 'validation_maximum', 'validation_regex',
+            'choice_set', 'created', 'last_updated',
         ]
         brief_fields = ('id', 'url', 'display', 'name', 'description')
 

+ 4 - 0
netbox/extras/filtersets.py

@@ -132,6 +132,10 @@ class CustomFieldFilterSet(BaseFilterSet):
     object_type = ContentTypeFilter(
         field_name='object_types'
     )
+    related_object_type_id = MultiValueNumberFilter(
+        field_name='related_object_type__id'
+    )
+    related_object_type = ContentTypeFilter()
     choice_set_id = django_filters.ModelMultipleChoiceFilter(
         queryset=CustomFieldChoiceSet.objects.all()
     )

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

@@ -40,7 +40,7 @@ class CustomFieldImportForm(CSVModelForm):
         choices=CustomFieldTypeChoices,
         help_text=_('Field data type (e.g. text, integer, etc.)')
     )
-    object_type = CSVContentTypeField(
+    related_object_type = CSVContentTypeField(
         label=_('Object type'),
         queryset=ObjectType.objects.public(),
         required=False,
@@ -69,7 +69,7 @@ class CustomFieldImportForm(CSVModelForm):
     class Meta:
         model = CustomField
         fields = (
-            'name', 'label', 'group_name', 'type', 'object_types', 'object_type', 'required', 'description',
+            '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', 'ui_visible', 'ui_editable', 'is_cloneable',
         )

+ 4 - 4
netbox/extras/forms/filtersets.py

@@ -38,14 +38,14 @@ class CustomFieldFilterForm(SavedFiltersMixin, FilterForm):
     fieldsets = (
         (None, ('q', 'filter_id')),
         (_('Attributes'), (
-            'type', 'object_type_id', 'group_name', 'weight', 'required', 'choice_set_id', 'ui_visible', 'ui_editable',
-            'is_cloneable',
+            'type', 'related_object_type_id', 'group_name', 'weight', 'required', 'choice_set_id', 'ui_visible',
+            'ui_editable', 'is_cloneable',
         )),
     )
-    object_type_id = ContentTypeMultipleChoiceField(
+    related_object_type_id = ContentTypeMultipleChoiceField(
         queryset=ObjectType.objects.with_feature('custom_fields'),
         required=False,
-        label=_('Object type')
+        label=_('Related object type')
     )
     type = forms.MultipleChoiceField(
         choices=CustomFieldTypeChoices,

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

@@ -42,8 +42,8 @@ class CustomFieldForm(forms.ModelForm):
         label=_('Object types'),
         queryset=ObjectType.objects.with_feature('custom_fields')
     )
-    object_type = ContentTypeChoiceField(
-        label=_('Object type'),
+    related_object_type = ContentTypeChoiceField(
+        label=_('Related object type'),
         queryset=ObjectType.objects.public(),
         required=False,
         help_text=_("Type of the related object (for object/multi-object fields only)")
@@ -55,7 +55,7 @@ class CustomFieldForm(forms.ModelForm):
 
     fieldsets = (
         (_('Custom Field'), (
-            'object_types', 'name', 'label', 'group_name', 'type', 'object_type', 'required', 'description',
+            'object_types', 'name', 'label', 'group_name', 'type', 'related_object_type', 'required', 'description',
         )),
         (_('Behavior'), ('search_weight', 'filter_logic', 'ui_visible', 'ui_editable', 'weight', 'is_cloneable')),
         (_('Values'), ('default', 'choice_set')),

+ 16 - 0
netbox/extras/migrations/0113_customfield_rename_object_type.py

@@ -0,0 +1,16 @@
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('extras', '0112_tag_update_object_types'),
+    ]
+
+    operations = [
+        migrations.RenameField(
+            model_name='customfield',
+            old_name='object_type',
+            new_name='related_object_type',
+        ),
+    ]

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

@@ -78,7 +78,7 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
         default=CustomFieldTypeChoices.TYPE_TEXT,
         help_text=_('The type of data this custom field holds')
     )
-    object_type = models.ForeignKey(
+    related_object_type = models.ForeignKey(
         to='core.ObjectType',
         on_delete=models.PROTECT,
         blank=True,
@@ -209,7 +209,7 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
     objects = CustomFieldManager()
 
     clone_fields = (
-        'object_types', 'type', 'object_type', 'group_name', 'description', 'required', 'search_weight',
+        'object_types', 'type', 'related_object_type', 'group_name', 'description', 'required', 'search_weight',
         'filter_logic', 'default', 'weight', 'validation_minimum', 'validation_maximum', 'validation_regex',
         'choice_set', 'ui_visible', 'ui_editable', 'is_cloneable',
     )
@@ -344,11 +344,11 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
 
         # Object fields must define an object_type; other fields must not
         if self.type in (CustomFieldTypeChoices.TYPE_OBJECT, CustomFieldTypeChoices.TYPE_MULTIOBJECT):
-            if not self.object_type:
+            if not self.related_object_type:
                 raise ValidationError({
                     'object_type': _("Object fields must define an object type.")
                 })
-        elif self.object_type:
+        elif self.related_object_type:
             raise ValidationError({
                 'object_type': _(
                     "{type} fields may not define an object type.")
@@ -388,10 +388,10 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
             except ValueError:
                 return value
         if self.type == CustomFieldTypeChoices.TYPE_OBJECT:
-            model = self.object_type.model_class()
+            model = self.related_object_type.model_class()
             return model.objects.filter(pk=value).first()
         if self.type == CustomFieldTypeChoices.TYPE_MULTIOBJECT:
-            model = self.object_type.model_class()
+            model = self.related_object_type.model_class()
             return model.objects.filter(pk__in=value)
         return value
 
@@ -488,7 +488,7 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
 
         # Object
         elif self.type == CustomFieldTypeChoices.TYPE_OBJECT:
-            model = self.object_type.model_class()
+            model = self.related_object_type.model_class()
             field_class = CSVModelChoiceField if for_csv_import else DynamicModelChoiceField
             field = field_class(
                 queryset=model.objects.all(),
@@ -498,7 +498,7 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
 
         # Multiple objects
         elif self.type == CustomFieldTypeChoices.TYPE_MULTIOBJECT:
-            model = self.object_type.model_class()
+            model = self.related_object_type.model_class()
             field_class = CSVModelMultipleChoiceField if for_csv_import else DynamicModelMultipleChoiceField
             field = field_class(
                 queryset=model.objects.all(),

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

@@ -57,6 +57,9 @@ class CustomFieldTable(NetBoxTable):
     description = columns.MarkdownColumn(
         verbose_name=_('Description')
     )
+    related_object_type = columns.ContentTypeColumn(
+        verbose_name=_('Related Object Type')
+    )
     choice_set = tables.Column(
         linkify=True,
         verbose_name=_('Choice Set')
@@ -73,9 +76,9 @@ class CustomFieldTable(NetBoxTable):
     class Meta(NetBoxTable.Meta):
         model = CustomField
         fields = (
-            'pk', 'id', 'name', 'object_types', 'label', 'type', 'group_name', 'required', 'default', 'description',
-            'search_weight', 'filter_logic', 'ui_visible', 'ui_editable', 'is_cloneable', 'weight', 'choice_set',
-            'choices', 'created', 'last_updated',
+            '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', 'created', 'last_updated',
         )
         default_columns = ('pk', 'name', 'object_types', 'label', 'group_name', 'type', 'required', 'description')
 

+ 23 - 11
netbox/extras/tests/test_customfields.py

@@ -350,7 +350,7 @@ class CustomFieldTest(TestCase):
         cf = CustomField.objects.create(
             name='object_field',
             type=CustomFieldTypeChoices.TYPE_OBJECT,
-            object_type=ObjectType.objects.get_for_model(VLAN),
+            related_object_type=ObjectType.objects.get_for_model(VLAN),
             required=False
         )
         cf.object_types.set([self.object_type])
@@ -382,7 +382,7 @@ class CustomFieldTest(TestCase):
         cf = CustomField.objects.create(
             name='object_field',
             type=CustomFieldTypeChoices.TYPE_MULTIOBJECT,
-            object_type=ObjectType.objects.get_for_model(VLAN),
+            related_object_type=ObjectType.objects.get_for_model(VLAN),
             required=False
         )
         cf.object_types.set([self.object_type])
@@ -498,16 +498,28 @@ class CustomFieldTest(TestCase):
             ).full_clean()
 
         # Object
-        CustomField(name='test', type='object', required=True, object_type=object_type, default=site.pk).full_clean()
-        with self.assertRaises(ValidationError):
-            CustomField(name='test', type='object', required=True, object_type=object_type, default="xxx").full_clean()
+        CustomField(
+            name='test',
+            type='object',
+            required=True,
+            related_object_type=object_type,
+            default=site.pk
+        ).full_clean()
+        with (self.assertRaises(ValidationError)):
+            CustomField(
+                name='test',
+                type='object',
+                required=True,
+                related_object_type=object_type,
+                default="xxx"
+            ).full_clean()
 
         # Multi-object
         CustomField(
             name='test',
             type='multiobject',
             required=True,
-            object_type=object_type,
+            related_object_type=object_type,
             default=[site.pk]
         ).full_clean()
         with self.assertRaises(ValidationError):
@@ -515,7 +527,7 @@ class CustomFieldTest(TestCase):
                 name='test',
                 type='multiobject',
                 required=True,
-                object_type=object_type,
+                related_object_type=object_type,
                 default=["xxx"]
             ).full_clean()
 
@@ -581,13 +593,13 @@ class CustomFieldAPITest(APITestCase):
             CustomField(
                 type=CustomFieldTypeChoices.TYPE_OBJECT,
                 name='object_field',
-                object_type=ObjectType.objects.get_for_model(VLAN),
+                related_object_type=ObjectType.objects.get_for_model(VLAN),
                 default=vlans[0].pk,
             ),
             CustomField(
                 type=CustomFieldTypeChoices.TYPE_MULTIOBJECT,
                 name='multiobject_field',
-                object_type=ObjectType.objects.get_for_model(VLAN),
+                related_object_type=ObjectType.objects.get_for_model(VLAN),
                 default=[vlans[0].pk, vlans[1].pk],
             ),
         )
@@ -1410,7 +1422,7 @@ class CustomFieldModelFilterTest(TestCase):
         cf = CustomField(
             name='cf11',
             type=CustomFieldTypeChoices.TYPE_OBJECT,
-            object_type=ObjectType.objects.get_for_model(Manufacturer)
+            related_object_type=ObjectType.objects.get_for_model(Manufacturer)
         )
         cf.save()
         cf.object_types.set([object_type])
@@ -1419,7 +1431,7 @@ class CustomFieldModelFilterTest(TestCase):
         cf = CustomField(
             name='cf12',
             type=CustomFieldTypeChoices.TYPE_MULTIOBJECT,
-            object_type=ObjectType.objects.get_for_model(Manufacturer)
+            related_object_type=ObjectType.objects.get_for_model(Manufacturer)
         )
         cf.save()
         cf.object_types.set([object_type])

+ 16 - 0
netbox/extras/tests/test_filtersets.py

@@ -86,6 +86,16 @@ class CustomFieldTestCase(TestCase, BaseFilterSetTests):
                 ui_editable=CustomFieldUIEditableChoices.HIDDEN,
                 choice_set=choice_sets[1]
             ),
+            CustomField(
+                name='Custom Field 6',
+                type=CustomFieldTypeChoices.TYPE_OBJECT,
+                related_object_type=ObjectType.objects.get_by_natural_key('dcim', 'site'),
+                required=False,
+                weight=600,
+                filter_logic=CustomFieldFilterLogicChoices.FILTER_DISABLED,
+                ui_visible=CustomFieldUIVisibleChoices.HIDDEN,
+                ui_editable=CustomFieldUIEditableChoices.HIDDEN
+            ),
         )
         CustomField.objects.bulk_create(custom_fields)
         custom_fields[0].object_types.add(ObjectType.objects.get_by_natural_key('dcim', 'site'))
@@ -108,6 +118,12 @@ class CustomFieldTestCase(TestCase, BaseFilterSetTests):
         params = {'object_type_id': [ObjectType.objects.get_by_natural_key('dcim', 'site').pk]}
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
 
+    def test_related_object_type(self):
+        params = {'related_object_type': 'dcim.site'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+        params = {'related_object_type_id': [ObjectType.objects.get_by_natural_key('dcim', 'site').pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
     def test_required(self):
         params = {'required': True}
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)

+ 2 - 2
netbox/extras/tests/test_forms.py

@@ -62,14 +62,14 @@ class CustomFieldModelFormTest(TestCase):
         cf_object = CustomField.objects.create(
             name='object',
             type=CustomFieldTypeChoices.TYPE_OBJECT,
-            object_type=ObjectType.objects.get_for_model(Site)
+            related_object_type=ObjectType.objects.get_for_model(Site)
         )
         cf_object.object_types.set([object_type])
 
         cf_multiobject = CustomField.objects.create(
             name='multiobject',
             type=CustomFieldTypeChoices.TYPE_MULTIOBJECT,
-            object_type=ObjectType.objects.get_for_model(Site)
+            related_object_type=ObjectType.objects.get_for_model(Site)
         )
         cf_multiobject.object_types.set([object_type])
 

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

@@ -54,7 +54,7 @@ class CustomFieldTestCase(ViewTestCases.PrimaryObjectViewTestCase):
         }
 
         cls.csv_data = (
-            'name,label,type,object_types,object_type,weight,search_weight,filter_logic,choice_set,validation_minimum,validation_maximum,validation_regex,ui_visible,ui_editable',
+            'name,label,type,object_types,related_object_type,weight,search_weight,filter_logic,choice_set,validation_minimum,validation_maximum,validation_regex,ui_visible,ui_editable',
             'field4,Field 4,text,dcim.site,,100,1000,exact,,,,[a-z]{3},always,yes',
             'field5,Field 5,integer,dcim.site,,100,2000,exact,,1,100,,always,yes',
             'field6,Field 6,select,dcim.site,,100,3000,exact,Choice Set 1,,,,always,yes',

+ 3 - 1
netbox/templates/extras/customfield.html

@@ -17,7 +17,9 @@
           <th scope="row">Type</th>
           <td>
             {{ object.get_type_display }}
-            {% if object.object_type %}({{ object.object_type.model|bettertitle }}){% endif %}
+            {% if object.related_object_type %}
+              ({{ object.related_object_type.model|bettertitle }})
+            {% endif %}
           </td>
         </tr>
         <tr>