Просмотр исходного кода

Closes: #16837 - Fix type__empty filter in character-based filters (#17574)

* Fix type__empty filter in character-based filters

* Add tests
bctiemann 1 год назад
Родитель
Сommit
116a423d8f
2 измененных файлов с 9 добавлено и 2 удалено
  1. 4 0
      netbox/dcim/tests/test_filtersets.py
  2. 5 2
      netbox/netbox/filtersets.py

+ 4 - 0
netbox/dcim/tests/test_filtersets.py

@@ -5247,6 +5247,10 @@ class CableTestCase(TestCase, ChangeLoggedFilterSetTests):
     def test_type(self):
         params = {'type': [CableTypeChoices.TYPE_CAT3, CableTypeChoices.TYPE_CAT5E]}
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+        params = {'type__empty': 'true'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 8)
+        params = {'type__empty': 'false'}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 6)
 
     def test_status(self):
         params = {'status': [LinkStatusChoices.STATUS_CONNECTED]}

+ 5 - 2
netbox/netbox/filtersets.py

@@ -133,7 +133,7 @@ class BaseFilterSet(django_filters.FilterSet):
             django_filters.ModelChoiceFilter,
             django_filters.ModelMultipleChoiceFilter,
             TagFilter
-        )) or existing_filter.extra.get('choices'):
+        )):
             # These filter types support only negation
             return FILTER_NEGATION_LOOKUP_MAP
 
@@ -172,6 +172,7 @@ class BaseFilterSet(django_filters.FilterSet):
         # Create new filters for each lookup expression in the map
         for lookup_name, lookup_expr in lookup_map.items():
             new_filter_name = f'{existing_filter_name}__{lookup_name}'
+            existing_filter_extra = deepcopy(existing_filter.extra)
 
             try:
                 if existing_filter_name in cls.declared_filters:
@@ -179,6 +180,8 @@ class BaseFilterSet(django_filters.FilterSet):
                     # create the new filter with the same type because there is no guarantee the defined type
                     # is the same as the default type for the field
                     resolve_field(field, lookup_expr)  # Will raise FieldLookupError if the lookup is invalid
+                    for field_to_remove in ('choices', 'null_value'):
+                        existing_filter_extra.pop(field_to_remove, None)
                     filter_cls = django_filters.BooleanFilter if lookup_expr == 'empty' else type(existing_filter)
                     new_filter = filter_cls(
                         field_name=field_name,
@@ -186,7 +189,7 @@ class BaseFilterSet(django_filters.FilterSet):
                         label=existing_filter.label,
                         exclude=existing_filter.exclude,
                         distinct=existing_filter.distinct,
-                        **existing_filter.extra
+                        **existing_filter_extra
                     )
                 elif hasattr(existing_filter, 'custom_field'):
                     # Filter is for a custom field