From 7c60089692d853c7db2bb3af623bf28347697596 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Thu, 28 Oct 2021 15:31:50 -0400 Subject: [PATCH] Ditch CustomFieldFilter --- netbox/extras/filters.py | 36 --------------------- netbox/extras/filtersets.py | 7 ---- netbox/extras/models/customfields.py | 48 ++++++++++++++++++++++++++++ netbox/netbox/filtersets.py | 9 +++--- 4 files changed, 53 insertions(+), 47 deletions(-) diff --git a/netbox/extras/filters.py b/netbox/extras/filters.py index b37aaf40e..de739aa59 100644 --- a/netbox/extras/filters.py +++ b/netbox/extras/filters.py @@ -1,47 +1,11 @@ import django_filters -from django.forms import DateField, IntegerField, NullBooleanField from .models import Tag -from .choices import * __all__ = ( - 'CustomFieldFilter', 'TagFilter', ) -EXACT_FILTER_TYPES = ( - CustomFieldTypeChoices.TYPE_BOOLEAN, - CustomFieldTypeChoices.TYPE_DATE, - CustomFieldTypeChoices.TYPE_INTEGER, - CustomFieldTypeChoices.TYPE_SELECT, - CustomFieldTypeChoices.TYPE_MULTISELECT, -) - - -class CustomFieldFilter(django_filters.Filter): - """ - Filter objects by the presence of a CustomFieldValue. The filter's name is used as the CustomField name. - """ - def __init__(self, custom_field, *args, **kwargs): - self.custom_field = custom_field - - if custom_field.type == CustomFieldTypeChoices.TYPE_INTEGER: - self.field_class = IntegerField - elif custom_field.type == CustomFieldTypeChoices.TYPE_BOOLEAN: - self.field_class = NullBooleanField - elif custom_field.type == CustomFieldTypeChoices.TYPE_DATE: - self.field_class = DateField - - super().__init__(*args, **kwargs) - - self.field_name = f'custom_field_data__{self.field_name}' - - if custom_field.type == CustomFieldTypeChoices.TYPE_MULTISELECT: - self.lookup_expr = 'has_key' - elif custom_field.type not in EXACT_FILTER_TYPES: - if custom_field.filter_logic == CustomFieldFilterLogicChoices.FILTER_LOOSE: - self.lookup_expr = 'icontains' - class TagFilter(django_filters.ModelMultipleChoiceFilter): """ diff --git a/netbox/extras/filtersets.py b/netbox/extras/filtersets.py index af8d904f4..0d44eab57 100644 --- a/netbox/extras/filtersets.py +++ b/netbox/extras/filtersets.py @@ -26,13 +26,6 @@ __all__ = ( 'WebhookFilterSet', ) -EXACT_FILTER_TYPES = ( - CustomFieldTypeChoices.TYPE_BOOLEAN, - CustomFieldTypeChoices.TYPE_DATE, - CustomFieldTypeChoices.TYPE_INTEGER, - CustomFieldTypeChoices.TYPE_SELECT, -) - class WebhookFilterSet(BaseFilterSet): content_types = ContentTypeFilter() diff --git a/netbox/extras/models/customfields.py b/netbox/extras/models/customfields.py index bc6458039..a889762f9 100644 --- a/netbox/extras/models/customfields.py +++ b/netbox/extras/models/customfields.py @@ -8,6 +8,7 @@ from django.core.validators import RegexValidator, ValidationError from django.db import models from django.urls import reverse from django.utils.safestring import mark_safe +from django_filters import filters from extras.choices import * from extras.utils import FeatureQuery, extras_features @@ -308,6 +309,53 @@ class CustomField(ChangeLoggedModel): return field + def to_filter(self): + """ + Return a django_filters Filter instance suitable for this field type. + """ + kwargs = { + 'field_name': f'custom_field_data__{self.name}' + } + + # Text/URL + if self.type in ( + CustomFieldTypeChoices.TYPE_TEXT, + CustomFieldTypeChoices.TYPE_LONGTEXT, + CustomFieldTypeChoices.TYPE_URL, + ): + filter_class = filters.CharFilter + if self.filter_logic == CustomFieldFilterLogicChoices.FILTER_LOOSE: + kwargs['lookup_expr'] = 'icontains' + + # Integer + elif self.type == CustomFieldTypeChoices.TYPE_INTEGER: + # TODO: Remove dirty hack to change lookup type from Decimal + filter_class = filters.NumberFilter + filter_class.field_class = forms.IntegerField + + # Boolean + elif self.type == CustomFieldTypeChoices.TYPE_BOOLEAN: + filter_class = filters.BooleanFilter + + # Date + elif self.type == CustomFieldTypeChoices.TYPE_DATE: + filter_class = filters.DateFilter + + # Select + elif self.type == CustomFieldTypeChoices.TYPE_SELECT: + filter_class = filters.CharFilter + + # Multiselect + elif self.type == CustomFieldTypeChoices.TYPE_MULTISELECT: + filter_class = filters.CharFilter + kwargs['lookup_expr'] = 'has_key' + + # Unsupported custom field type + else: + return None + + return filter_class(**kwargs) + def validate(self, value): """ Validate a value according to the field's type validation rules. diff --git a/netbox/netbox/filtersets.py b/netbox/netbox/filtersets.py index 879f57bdc..ea0b9eda0 100644 --- a/netbox/netbox/filtersets.py +++ b/netbox/netbox/filtersets.py @@ -7,7 +7,7 @@ from django_filters.utils import get_model_field, resolve_field from dcim.forms import MACAddressField from extras.choices import CustomFieldFilterLogicChoices -from extras.filters import CustomFieldFilter, TagFilter +from extras.filters import TagFilter from extras.models import CustomField from utilities.constants import ( FILTER_CHAR_BASED_LOOKUP_MAP, FILTER_NEGATION_LOOKUP_MAP, FILTER_TREENODE_NEGATION_LOOKUP_MAP, @@ -15,7 +15,6 @@ from utilities.constants import ( ) from utilities import filters - __all__ = ( 'BaseFilterSet', 'ChangeLoggedModelFilterSet', @@ -222,8 +221,10 @@ class PrimaryModelFilterSet(ChangeLoggedModelFilterSet): ) custom_field_filters = {} - for cf in custom_fields: - custom_field_filters[f'cf_{cf.name}'] = CustomFieldFilter(field_name=cf.name, custom_field=cf) + for custom_field in custom_fields: + cf_filter = custom_field.to_filter() + if cf_filter: + custom_field_filters[f'cf_{custom_field.name}'] = cf_filter self.filters.update(custom_field_filters)