Ditch CustomFieldFilter

This commit is contained in:
jeremystretch 2021-10-28 15:31:50 -04:00
parent 6377d475fc
commit 7c60089692
4 changed files with 53 additions and 47 deletions

View File

@ -1,47 +1,11 @@
import django_filters import django_filters
from django.forms import DateField, IntegerField, NullBooleanField
from .models import Tag from .models import Tag
from .choices import *
__all__ = ( __all__ = (
'CustomFieldFilter',
'TagFilter', '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): class TagFilter(django_filters.ModelMultipleChoiceFilter):
""" """

View File

@ -26,13 +26,6 @@ __all__ = (
'WebhookFilterSet', 'WebhookFilterSet',
) )
EXACT_FILTER_TYPES = (
CustomFieldTypeChoices.TYPE_BOOLEAN,
CustomFieldTypeChoices.TYPE_DATE,
CustomFieldTypeChoices.TYPE_INTEGER,
CustomFieldTypeChoices.TYPE_SELECT,
)
class WebhookFilterSet(BaseFilterSet): class WebhookFilterSet(BaseFilterSet):
content_types = ContentTypeFilter() content_types = ContentTypeFilter()

View File

@ -8,6 +8,7 @@ from django.core.validators import RegexValidator, ValidationError
from django.db import models from django.db import models
from django.urls import reverse from django.urls import reverse
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django_filters import filters
from extras.choices import * from extras.choices import *
from extras.utils import FeatureQuery, extras_features from extras.utils import FeatureQuery, extras_features
@ -308,6 +309,53 @@ class CustomField(ChangeLoggedModel):
return field 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): def validate(self, value):
""" """
Validate a value according to the field's type validation rules. Validate a value according to the field's type validation rules.

View File

@ -7,7 +7,7 @@ from django_filters.utils import get_model_field, resolve_field
from dcim.forms import MACAddressField from dcim.forms import MACAddressField
from extras.choices import CustomFieldFilterLogicChoices from extras.choices import CustomFieldFilterLogicChoices
from extras.filters import CustomFieldFilter, TagFilter from extras.filters import TagFilter
from extras.models import CustomField from extras.models import CustomField
from utilities.constants import ( from utilities.constants import (
FILTER_CHAR_BASED_LOOKUP_MAP, FILTER_NEGATION_LOOKUP_MAP, FILTER_TREENODE_NEGATION_LOOKUP_MAP, 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 from utilities import filters
__all__ = ( __all__ = (
'BaseFilterSet', 'BaseFilterSet',
'ChangeLoggedModelFilterSet', 'ChangeLoggedModelFilterSet',
@ -222,8 +221,10 @@ class PrimaryModelFilterSet(ChangeLoggedModelFilterSet):
) )
custom_field_filters = {} custom_field_filters = {}
for cf in custom_fields: for custom_field in custom_fields:
custom_field_filters[f'cf_{cf.name}'] = CustomFieldFilter(field_name=cf.name, custom_field=cf) 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) self.filters.update(custom_field_filters)