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

Closes #6138: Add an 'empty' filter modifier for character fields

jeremystretch 4 лет назад
Родитель
Сommit
76a6119584

+ 1 - 0
docs/release-notes/version-2.11.md

@@ -4,6 +4,7 @@
 
 ### Enhancements
 
+* [#6138](https://github.com/netbox-community/netbox/issues/6138) - Add an `empty` filter modifier for character fields
 * [#6620](https://github.com/netbox-community/netbox/issues/6620) - Show assigned VMs count under device role view
 * [#6666](https://github.com/netbox-community/netbox/issues/6666) - Show management-only status under interface detail view
 * [#6667](https://github.com/netbox-community/netbox/issues/6667) - Display VM memory as GB/TB as appropriate

+ 19 - 14
docs/rest-api/filtering.md

@@ -61,25 +61,30 @@ These lookup expressions can be applied by adding a suffix to the desired field'
 
 Numeric based fields (ASN, VLAN ID, etc) support these lookup expressions:
 
-- `n` - not equal to (negation)
-- `lt` - less than
-- `lte` - less than or equal
-- `gt` - greater than
-- `gte` - greater than or equal
+| Filter | Description |
+|--------|-------------|
+| `n` | Not equal to |
+| `lt` | Less than |
+| `lte` | Less than or equal to |
+| `gt` | Greater than |
+| `gte` | Greater than or equal to |
 
 ### String Fields
 
 String based (char) fields (Name, Address, etc) support these lookup expressions:
 
-- `n` - not equal to (negation)
-- `ic` - case insensitive contains
-- `nic` - negated case insensitive contains
-- `isw` - case insensitive starts with
-- `nisw` - negated case insensitive starts with
-- `iew` - case insensitive ends with
-- `niew` - negated case insensitive ends with
-- `ie` - case insensitive exact match
-- `nie` - negated case insensitive exact match
+| Filter | Description |
+|--------|-------------|
+| `n` | Not equal to |
+| `ic` | Contains (case-insensitive) |
+| `nic` | Does not contain (case-insensitive) |
+| `isw` | Starts with (case-insensitive) |
+| `nisw` | Does not start with (case-insensitive) |
+| `iew` | Ends with (case-insensitive) |
+| `niew` | Does not end with (case-insensitive) |
+| `ie` | Exact match (case-insensitive) |
+| `nie` | Inverse exact match (case-insensitive) |
+| `empty` | Is empty (boolean) |
 
 ### Foreign Keys & Other Fields
 

+ 1 - 0
netbox/extras/apps.py

@@ -5,4 +5,5 @@ class ExtrasConfig(AppConfig):
     name = "extras"
 
     def ready(self):
+        import extras.lookups
         import extras.signals

+ 17 - 0
netbox/extras/lookups.py

@@ -0,0 +1,17 @@
+from django.db.models import CharField, Lookup
+
+
+class Empty(Lookup):
+    """
+    Filter on whether a string is empty.
+    """
+    lookup_name = 'empty'
+
+    def as_sql(self, qn, connection):
+        lhs, lhs_params = self.process_lhs(qn, connection)
+        rhs, rhs_params = self.process_rhs(qn, connection)
+        params = lhs_params + rhs_params
+        return 'CAST(LENGTH(%s) AS BOOLEAN) != %s' % (lhs, rhs), params
+
+
+CharField.register_lookup(Empty)

+ 5 - 8
netbox/netbox/filtersets.py

@@ -89,13 +89,13 @@ class BaseFilterSet(django_filters.FilterSet):
             filters.MultiValueNumberFilter,
             filters.MultiValueTimeFilter
         )):
-            lookup_map = FILTER_NUMERIC_BASED_LOOKUP_MAP
+            return FILTER_NUMERIC_BASED_LOOKUP_MAP
 
         elif isinstance(existing_filter, (
             filters.TreeNodeMultipleChoiceFilter,
         )):
             # TreeNodeMultipleChoiceFilter only support negation but must maintain the `in` lookup expression
-            lookup_map = FILTER_TREENODE_NEGATION_LOOKUP_MAP
+            return FILTER_TREENODE_NEGATION_LOOKUP_MAP
 
         elif isinstance(existing_filter, (
             django_filters.ModelChoiceFilter,
@@ -103,7 +103,7 @@ class BaseFilterSet(django_filters.FilterSet):
             TagFilter
         )) or existing_filter.extra.get('choices'):
             # These filter types support only negation
-            lookup_map = FILTER_NEGATION_LOOKUP_MAP
+            return FILTER_NEGATION_LOOKUP_MAP
 
         elif isinstance(existing_filter, (
             django_filters.filters.CharFilter,
@@ -111,12 +111,9 @@ class BaseFilterSet(django_filters.FilterSet):
             filters.MultiValueCharFilter,
             filters.MultiValueMACAddressFilter
         )):
-            lookup_map = FILTER_CHAR_BASED_LOOKUP_MAP
+            return FILTER_CHAR_BASED_LOOKUP_MAP
 
-        else:
-            lookup_map = None
-
-        return lookup_map
+        return None
 
     @classmethod
     def get_filters(cls):

+ 2 - 1
netbox/utilities/constants.py

@@ -11,7 +11,8 @@ FILTER_CHAR_BASED_LOOKUP_MAP = dict(
     isw='istartswith',
     nisw='istartswith',
     ie='iexact',
-    nie='iexact'
+    nie='iexact',
+    empty='empty',
 )
 
 FILTER_NUMERIC_BASED_LOOKUP_MAP = dict(