From c2b36df3771dea3c161d10755495a44b3a0fa30c Mon Sep 17 00:00:00 2001 From: Sam King Date: Fri, 26 Jul 2024 16:53:06 +1000 Subject: [PATCH] Fixes #16782: Add object filtering for custom fields --- docs/customization/custom-fields.md | 2 ++ netbox/extras/api/serializers_/customfields.py | 2 +- netbox/extras/forms/model_forms.py | 2 +- .../0120_customfield_related_object_filter.py | 18 ++++++++++++++++++ netbox/extras/models/customfields.py | 12 +++++++++++- netbox/extras/tests/test_filtersets.py | 2 +- netbox/templates/extras/customfield.html | 4 ++++ 7 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 netbox/extras/migrations/0120_customfield_related_object_filter.py diff --git a/docs/customization/custom-fields.md b/docs/customization/custom-fields.md index 1f9a4a8bf..1e500d203 100644 --- a/docs/customization/custom-fields.md +++ b/docs/customization/custom-fields.md @@ -74,6 +74,8 @@ If a default value is specified for a selection field, it must exactly match one An object or multi-object custom field can be used to refer to a particular NetBox object or objects as the "value" for a custom field. These custom fields must define an `object_type`, which determines the type of object to which custom field instances point. +By default, an object choice field will make all objects of that type available for selection in the drop-down. The list choices can be filtered to only show objects with certain values by providing a `query_params` dict in the Related Object Filter field, as a JSON value. More information about `query_params` can be found [here](./custom-scripts.md#objectvar) + ## Custom Fields in Templates Several features within NetBox, such as export templates and webhooks, utilize Jinja2 templating. For convenience, objects which support custom field assignment expose custom field data through the `cf` property. This is a bit cleaner than accessing custom field data through the actual field (`custom_field_data`). diff --git a/netbox/extras/api/serializers_/customfields.py b/netbox/extras/api/serializers_/customfields.py index 9675cb173..2c8e7c127 100644 --- a/netbox/extras/api/serializers_/customfields.py +++ b/netbox/extras/api/serializers_/customfields.py @@ -62,7 +62,7 @@ class CustomFieldSerializer(ValidatedModelSerializer): 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', 'weight', 'validation_minimum', 'validation_maximum', + 'ui_editable', 'is_cloneable', 'default', 'related_object_filter', 'weight', 'validation_minimum', 'validation_maximum', 'validation_regex', 'validation_unique', 'choice_set', 'comments', 'created', 'last_updated', ] brief_fields = ('id', 'url', 'display', 'name', 'description') diff --git a/netbox/extras/forms/model_forms.py b/netbox/extras/forms/model_forms.py index b4221b960..ce013f7c6 100644 --- a/netbox/extras/forms/model_forms.py +++ b/netbox/extras/forms/model_forms.py @@ -67,7 +67,7 @@ class CustomFieldForm(forms.ModelForm): FieldSet( 'search_weight', 'filter_logic', 'ui_visible', 'ui_editable', 'weight', 'is_cloneable', name=_('Behavior') ), - FieldSet('default', 'choice_set', name=_('Values')), + FieldSet('default', 'choice_set', 'related_object_filter', name=_('Values')), FieldSet( 'validation_minimum', 'validation_maximum', 'validation_regex', 'validation_unique', name=_('Validation') ), diff --git a/netbox/extras/migrations/0120_customfield_related_object_filter.py b/netbox/extras/migrations/0120_customfield_related_object_filter.py new file mode 100644 index 000000000..431ce5bf9 --- /dev/null +++ b/netbox/extras/migrations/0120_customfield_related_object_filter.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.7 on 2024-07-26 01:49 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('extras', '0119_eventrule_event_types'), + ] + + operations = [ + migrations.AddField( + model_name='customfield', + name='related_object_filter', + field=models.JSONField(blank=True, null=True), + ), + ] diff --git a/netbox/extras/models/customfields.py b/netbox/extras/models/customfields.py index 1d84a3f4f..4ca16bfb7 100644 --- a/netbox/extras/models/customfields.py +++ b/netbox/extras/models/customfields.py @@ -154,6 +154,14 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel): 'Default value for the field (must be a JSON value). Encapsulate strings with double quotes (e.g. "Foo").' ) ) + related_object_filter = models.JSONField( + blank=True, + null=True, + help_text=_( + 'Filter the object selection choices using a query_params dict (must be a JSON value).' + 'Encapsulate strings with double quotes (e.g. "Foo").' + ) + ) weight = models.PositiveSmallIntegerField( default=100, verbose_name=_('display weight'), @@ -511,7 +519,8 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel): field = field_class( queryset=model.objects.all(), required=required, - initial=initial + initial=initial, + query_params=self.related_object_filter ) # Multiple objects @@ -522,6 +531,7 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel): queryset=model.objects.all(), required=required, initial=initial, + query_params=self.related_object_filter ) # Text diff --git a/netbox/extras/tests/test_filtersets.py b/netbox/extras/tests/test_filtersets.py index 144dec5d0..9048d5fd9 100644 --- a/netbox/extras/tests/test_filtersets.py +++ b/netbox/extras/tests/test_filtersets.py @@ -23,7 +23,7 @@ from virtualization.models import Cluster, ClusterGroup, ClusterType class CustomFieldTestCase(TestCase, ChangeLoggedFilterSetTests): queryset = CustomField.objects.all() filterset = CustomFieldFilterSet - ignore_fields = ('default',) + ignore_fields = ('default', 'related_object_filter') @classmethod def setUpTestData(cls): diff --git a/netbox/templates/extras/customfield.html b/netbox/templates/extras/customfield.html index fa0986778..85a3c6cd3 100644 --- a/netbox/templates/extras/customfield.html +++ b/netbox/templates/extras/customfield.html @@ -52,6 +52,10 @@ {% trans "Default Value" %} {{ object.default }} + + {% trans "Related object filter" %} + {{ object.related_object_filter }} +