Skip to content

Commit 116a423

Browse files
authored
Closes: #16837 - Fix type__empty filter in character-based filters (#17574)
* Fix type__empty filter in character-based filters * Add tests
1 parent 9c9c4fb commit 116a423

File tree

2 files changed

+9
-2
lines changed

2 files changed

+9
-2
lines changed

netbox/dcim/tests/test_filtersets.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5247,6 +5247,10 @@ def test_length_unit(self):
52475247
def test_type(self):
52485248
params = {'type': [CableTypeChoices.TYPE_CAT3, CableTypeChoices.TYPE_CAT5E]}
52495249
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
5250+
params = {'type__empty': 'true'}
5251+
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 8)
5252+
params = {'type__empty': 'false'}
5253+
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 6)
52505254

52515255
def test_status(self):
52525256
params = {'status': [LinkStatusChoices.STATUS_CONNECTED]}

netbox/netbox/filtersets.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ def _get_filter_lookup_dict(existing_filter):
133133
django_filters.ModelChoiceFilter,
134134
django_filters.ModelMultipleChoiceFilter,
135135
TagFilter
136-
)) or existing_filter.extra.get('choices'):
136+
)):
137137
# These filter types support only negation
138138
return FILTER_NEGATION_LOOKUP_MAP
139139

@@ -172,21 +172,24 @@ def get_additional_lookups(cls, existing_filter_name, existing_filter):
172172
# Create new filters for each lookup expression in the map
173173
for lookup_name, lookup_expr in lookup_map.items():
174174
new_filter_name = f'{existing_filter_name}__{lookup_name}'
175+
existing_filter_extra = deepcopy(existing_filter.extra)
175176

176177
try:
177178
if existing_filter_name in cls.declared_filters:
178179
# The filter field has been explicitly defined on the filterset class so we must manually
179180
# create the new filter with the same type because there is no guarantee the defined type
180181
# is the same as the default type for the field
181182
resolve_field(field, lookup_expr) # Will raise FieldLookupError if the lookup is invalid
183+
for field_to_remove in ('choices', 'null_value'):
184+
existing_filter_extra.pop(field_to_remove, None)
182185
filter_cls = django_filters.BooleanFilter if lookup_expr == 'empty' else type(existing_filter)
183186
new_filter = filter_cls(
184187
field_name=field_name,
185188
lookup_expr=lookup_expr,
186189
label=existing_filter.label,
187190
exclude=existing_filter.exclude,
188191
distinct=existing_filter.distinct,
189-
**existing_filter.extra
192+
**existing_filter_extra
190193
)
191194
elif hasattr(existing_filter, 'custom_field'):
192195
# Filter is for a custom field

0 commit comments

Comments
 (0)