diff --git a/docs/release-notes/version-3.3.md b/docs/release-notes/version-3.3.md index 7a5553b41..d4d2d35f5 100644 --- a/docs/release-notes/version-3.3.md +++ b/docs/release-notes/version-3.3.md @@ -8,6 +8,7 @@ * [#10435](https://github.com/netbox-community/netbox/issues/10435) - Fix exception when filtering VLANs by virtual machine with no cluster assigned * [#10439](https://github.com/netbox-community/netbox/issues/10439) - Fix form widget styling for DeviceType airflow field * [#10445](https://github.com/netbox-community/netbox/issues/10445) - Avoid rounding virtual machine memory values +* [#10461](https://github.com/netbox-community/netbox/issues/10461) - Enable filtering by read-only custom fields in the UI --- diff --git a/netbox/extras/forms/customfields.py b/netbox/extras/forms/customfields.py index 7574f4f2b..40d068450 100644 --- a/netbox/extras/forms/customfields.py +++ b/netbox/extras/forms/customfields.py @@ -34,7 +34,9 @@ class CustomFieldsMixin: return ContentType.objects.get_for_model(self.model) def _get_custom_fields(self, content_type): - return CustomField.objects.filter(content_types=content_type) + return CustomField.objects.filter(content_types=content_type).exclude( + ui_visibility=CustomFieldVisibilityChoices.VISIBILITY_HIDDEN + ) def _get_form_field(self, customfield): return customfield.to_form_field() @@ -50,13 +52,6 @@ class CustomFieldsMixin: field_name = f'cf_{customfield.name}' self.fields[field_name] = self._get_form_field(customfield) - if customfield.ui_visibility == CustomFieldVisibilityChoices.VISIBILITY_READ_ONLY: - self.fields[field_name].disabled = True - if self.fields[field_name].help_text: - self.fields[field_name].help_text += '
' - self.fields[field_name].help_text += ' ' \ - 'Field is set to read-only.' - # Annotate the field in the list of CustomField form fields self.custom_fields[field_name] = customfield if customfield.group_name not in self.custom_field_groups: diff --git a/netbox/extras/models/customfields.py b/netbox/extras/models/customfields.py index 43c4f9671..d52d73848 100644 --- a/netbox/extras/models/customfields.py +++ b/netbox/extras/models/customfields.py @@ -297,12 +297,13 @@ class CustomField(CloningMixin, ExportTemplatesMixin, WebhooksMixin, ChangeLogge return model.objects.filter(pk__in=value) return value - def to_form_field(self, set_initial=True, enforce_required=True, for_csv_import=False): + def to_form_field(self, set_initial=True, enforce_required=True, enforce_visibility=True, for_csv_import=False): """ Return a form field suitable for setting a CustomField's value for an object. set_initial: Set initial data for the field. This should be False when generating a field for bulk editing. enforce_required: Honor the value of CustomField.required. Set to False for filtering/bulk editing. + enforce_visibility: Honor the value of CustomField.ui_visibility. Set to False for filtering. for_csv_import: Return a form field suitable for bulk import of objects in CSV format. """ initial = self.default if set_initial else None @@ -398,6 +399,12 @@ class CustomField(CloningMixin, ExportTemplatesMixin, WebhooksMixin, ChangeLogge if self.description: field.help_text = escape(self.description) + # Annotate read-only fields + if enforce_visibility and self.ui_visibility == CustomFieldVisibilityChoices.VISIBILITY_READ_ONLY: + field.disabled = True + prepend = '
' if field.help_text else '' + field.help_text += f'{prepend} Field is set to read-only.' + return field def to_filter(self, lookup_expr=None): diff --git a/netbox/netbox/forms/base.py b/netbox/netbox/forms/base.py index 2676e4cde..fa741faf7 100644 --- a/netbox/netbox/forms/base.py +++ b/netbox/netbox/forms/base.py @@ -2,7 +2,7 @@ from django import forms from django.contrib.contenttypes.models import ContentType from django.db.models import Q -from extras.choices import CustomFieldFilterLogicChoices, CustomFieldTypeChoices +from extras.choices import CustomFieldFilterLogicChoices, CustomFieldTypeChoices, CustomFieldVisibilityChoices from extras.forms.customfields import CustomFieldsMixin from extras.models import CustomField, Tag from utilities.forms import BootstrapMixin, CSVModelForm @@ -125,10 +125,10 @@ class NetBoxModelFilterSetForm(BootstrapMixin, CustomFieldsMixin, forms.Form): ) def _get_custom_fields(self, content_type): - return CustomField.objects.filter(content_types=content_type).exclude( + return super()._get_custom_fields(content_type).exclude( Q(filter_logic=CustomFieldFilterLogicChoices.FILTER_DISABLED) | Q(type=CustomFieldTypeChoices.TYPE_JSON) ) def _get_form_field(self, customfield): - return customfield.to_form_field(set_initial=False, enforce_required=False) + return customfield.to_form_field(set_initial=False, enforce_required=False, enforce_visibility=False)