Skip to content

Commit

Permalink
Closes #15357: Rename CustomField.object_type to related_object_type
Browse files Browse the repository at this point in the history
  • Loading branch information
jeremystretch committed Mar 7, 2024
1 parent 7567c9d commit 380ac5c
Show file tree
Hide file tree
Showing 15 changed files with 97 additions and 44 deletions.
2 changes: 1 addition & 1 deletion docs/models/extras/customfield.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
6 changes: 3 additions & 3 deletions netbox/extras/api/customfields.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,10 @@ def to_representation(self, obj):
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

Expand All @@ -79,7 +79,7 @@ def to_internal_value(self, data):
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():
Expand Down
10 changes: 5 additions & 5 deletions netbox/extras/api/serializers_/customfields.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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')

Expand Down
4 changes: 4 additions & 0 deletions netbox/extras/filtersets.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
)
Expand Down
4 changes: 2 additions & 2 deletions netbox/extras/forms/bulk_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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',
)
Expand Down
8 changes: 4 additions & 4 deletions netbox/extras/forms/filtersets.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
6 changes: 3 additions & 3 deletions netbox/extras/forms/model_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)")
Expand All @@ -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')),
Expand Down
16 changes: 16 additions & 0 deletions netbox/extras/migrations/0113_customfield_rename_object_type.py
Original file line number Diff line number Diff line change
@@ -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',
),
]
16 changes: 8 additions & 8 deletions netbox/extras/models/customfields.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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',
)
Expand Down Expand Up @@ -344,11 +344,11 @@ def clean(self):

# 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.")
Expand Down Expand Up @@ -388,10 +388,10 @@ def deserialize(self, value):
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

Expand Down Expand Up @@ -488,7 +488,7 @@ def to_form_field(self, set_initial=True, enforce_required=True, enforce_visibil

# 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(),
Expand All @@ -498,7 +498,7 @@ def to_form_field(self, set_initial=True, enforce_required=True, enforce_visibil

# 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(),
Expand Down
9 changes: 6 additions & 3 deletions netbox/extras/tables/tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,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')
Expand All @@ -71,9 +74,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')

Expand Down
34 changes: 23 additions & 11 deletions netbox/extras/tests/test_customfields.py
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ def test_object_field(self):
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])
Expand Down Expand Up @@ -382,7 +382,7 @@ def test_multiobject_field(self):
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])
Expand Down Expand Up @@ -498,24 +498,36 @@ def test_default_value_validation(self):
).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):
CustomField(
name='test',
type='multiobject',
required=True,
object_type=object_type,
related_object_type=object_type,
default=["xxx"]
).full_clean()

Expand Down Expand Up @@ -581,13 +593,13 @@ def setUpTestData(cls):
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],
),
)
Expand Down Expand Up @@ -1410,7 +1422,7 @@ def setUpTestData(cls):
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])
Expand All @@ -1419,7 +1431,7 @@ def setUpTestData(cls):
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])
Expand Down
16 changes: 16 additions & 0 deletions netbox/extras/tests/test_filtersets.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,16 @@ def setUpTestData(cls):
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'))
Expand All @@ -108,6 +118,12 @@ def test_object_type(self):
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)
Expand Down
4 changes: 2 additions & 2 deletions netbox/extras/tests/test_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,14 @@ def setUpTestData(cls):
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])

Expand Down
2 changes: 1 addition & 1 deletion netbox/extras/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def setUpTestData(cls):
}

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',
Expand Down
4 changes: 3 additions & 1 deletion netbox/templates/extras/customfield.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ <h5 class="card-header">{% trans "Custom Field" %}</h5>
<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>
Expand Down

0 comments on commit 380ac5c

Please sign in to comment.