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

Merge pull request #7824 from netbox-community/2101-q-filters

Closes #2101: Ensure all relevant models have a general purpose search filter
Jeremy Stretch 4 лет назад
Родитель
Сommit
98cc36c458

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

@@ -4,6 +4,7 @@
 
 ### Enhancements
 
+* [#2101](https://github.com/netbox-community/netbox/issues/2101) - Add missing `q` filters for necessary models
 * [#7803](https://github.com/netbox-community/netbox/issues/7803) - Improve live reloading of custom scripts
 * [#7810](https://github.com/netbox-community/netbox/issues/7810) - Add IEEE 802.15.1 interface type
 

+ 13 - 0
netbox/dcim/filtersets.py

@@ -1394,6 +1394,10 @@ class PowerFeedFilterSet(PrimaryModelFilterSet, CableTerminationFilterSet, PathE
 #
 
 class ConnectionFilterSet(BaseFilterSet):
+    q = django_filters.CharFilter(
+        method='search',
+        label='Search',
+    )
     site_id = MultiValueNumberFilter(
         method='filter_connections',
         field_name='device__site_id'
@@ -1416,6 +1420,15 @@ class ConnectionFilterSet(BaseFilterSet):
             return queryset
         return queryset.filter(**{f'{name}__in': value})
 
+    def search(self, queryset, name, value):
+        if not value.strip():
+            return queryset
+        qs_filter = (
+            Q(device__name__icontains=value) |
+            Q(cable__label__icontains=value)
+        )
+        return queryset.filter(qs_filter)
+
 
 class ConsoleConnectionFilterSet(ConnectionFilterSet):
 

+ 15 - 0
netbox/dcim/forms/filtersets.py

@@ -1068,6 +1068,11 @@ class InventoryItemFilterForm(DeviceComponentFilterForm):
 #
 
 class ConsoleConnectionFilterForm(BootstrapMixin, forms.Form):
+    q = forms.CharField(
+        required=False,
+        widget=forms.TextInput(attrs={'placeholder': _('All Fields')}),
+        label=_('Search')
+    )
     region_id = DynamicModelMultipleChoiceField(
         queryset=Region.objects.all(),
         required=False,
@@ -1095,6 +1100,11 @@ class ConsoleConnectionFilterForm(BootstrapMixin, forms.Form):
 
 
 class PowerConnectionFilterForm(BootstrapMixin, forms.Form):
+    q = forms.CharField(
+        required=False,
+        widget=forms.TextInput(attrs={'placeholder': _('All Fields')}),
+        label=_('Search')
+    )
     region_id = DynamicModelMultipleChoiceField(
         queryset=Region.objects.all(),
         required=False,
@@ -1122,6 +1132,11 @@ class PowerConnectionFilterForm(BootstrapMixin, forms.Form):
 
 
 class InterfaceConnectionFilterForm(BootstrapMixin, forms.Form):
+    q = forms.CharField(
+        required=False,
+        widget=forms.TextInput(attrs={'placeholder': _('All Fields')}),
+        label=_('Search')
+    )
     region_id = DynamicModelMultipleChoiceField(
         queryset=Region.objects.all(),
         required=False,

+ 60 - 0
netbox/extras/filtersets.py

@@ -35,6 +35,10 @@ EXACT_FILTER_TYPES = (
 
 
 class WebhookFilterSet(BaseFilterSet):
+    q = django_filters.CharFilter(
+        method='search',
+        label='Search',
+    )
     content_types = ContentTypeFilter()
     http_method = django_filters.MultipleChoiceFilter(
         choices=WebhookHttpMethodChoices
@@ -47,30 +51,81 @@ class WebhookFilterSet(BaseFilterSet):
             'http_method', 'http_content_type', 'secret', 'ssl_verification', 'ca_file_path',
         ]
 
+    def search(self, queryset, name, value):
+        if not value.strip():
+            return queryset
+        return queryset.filter(
+            Q(name__icontains=value) |
+            Q(payload_url__icontains=value)
+        )
+
 
 class CustomFieldFilterSet(BaseFilterSet):
+    q = django_filters.CharFilter(
+        method='search',
+        label='Search',
+    )
     content_types = ContentTypeFilter()
 
     class Meta:
         model = CustomField
         fields = ['id', 'content_types', 'name', 'required', 'filter_logic', 'weight']
 
+    def search(self, queryset, name, value):
+        if not value.strip():
+            return queryset
+        return queryset.filter(
+            Q(name__icontains=value) |
+            Q(label__icontains=value) |
+            Q(description__icontains=value)
+        )
+
 
 class CustomLinkFilterSet(BaseFilterSet):
+    q = django_filters.CharFilter(
+        method='search',
+        label='Search',
+    )
 
     class Meta:
         model = CustomLink
         fields = ['id', 'content_type', 'name', 'link_text', 'link_url', 'weight', 'group_name', 'new_window']
 
+    def search(self, queryset, name, value):
+        if not value.strip():
+            return queryset
+        return queryset.filter(
+            Q(name__icontains=value) |
+            Q(link_text__icontains=value) |
+            Q(link_url__icontains=value) |
+            Q(group_name__icontains=value)
+        )
+
 
 class ExportTemplateFilterSet(BaseFilterSet):
+    q = django_filters.CharFilter(
+        method='search',
+        label='Search',
+    )
 
     class Meta:
         model = ExportTemplate
         fields = ['id', 'content_type', 'name']
 
+    def search(self, queryset, name, value):
+        if not value.strip():
+            return queryset
+        return queryset.filter(
+            Q(name__icontains=value) |
+            Q(description__icontains=value)
+        )
+
 
 class ImageAttachmentFilterSet(BaseFilterSet):
+    q = django_filters.CharFilter(
+        method='search',
+        label='Search',
+    )
     created = django_filters.DateTimeFilter()
     content_type = ContentTypeFilter()
 
@@ -78,6 +133,11 @@ class ImageAttachmentFilterSet(BaseFilterSet):
         model = ImageAttachment
         fields = ['id', 'content_type_id', 'object_id', 'name']
 
+    def search(self, queryset, name, value):
+        if not value.strip():
+            return queryset
+        return queryset.filter(name__icontains=value)
+
 
 class JournalEntryFilterSet(ChangeLoggedModelFilterSet):
     q = django_filters.CharFilter(

+ 20 - 0
netbox/users/filtersets.py

@@ -99,8 +99,20 @@ class TokenFilterSet(BaseFilterSet):
         model = Token
         fields = ['id', 'key', 'write_enabled']
 
+    def search(self, queryset, name, value):
+        if not value.strip():
+            return queryset
+        return queryset.filter(
+            Q(user__username__icontains=value) |
+            Q(description__icontains=value)
+        )
+
 
 class ObjectPermissionFilterSet(BaseFilterSet):
+    q = django_filters.CharFilter(
+        method='search',
+        label='Search',
+    )
     user_id = django_filters.ModelMultipleChoiceFilter(
         field_name='users',
         queryset=User.objects.all(),
@@ -127,3 +139,11 @@ class ObjectPermissionFilterSet(BaseFilterSet):
     class Meta:
         model = ObjectPermission
         fields = ['id', 'name', 'enabled', 'object_types']
+
+    def search(self, queryset, name, value):
+        if not value.strip():
+            return queryset
+        return queryset.filter(
+            Q(name__icontains=value) |
+            Q(description__icontains=value)
+        )