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

Closes #6931: Include applied filters on object list view

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

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

@@ -5,6 +5,7 @@
 ### Enhancements
 
 * [#6829](https://github.com/netbox-community/netbox/issues/6829) - Extend GraphQL API to support reverse generic relationships
+* [#6931](https://github.com/netbox-community/netbox/issues/6931) - Include applied filters on object list view
 
 ### Bug Fixes
 

+ 7 - 2
netbox/templates/generic/object_list.html

@@ -47,9 +47,14 @@
 
     {# Object list #}
     <div class="tab-pane show active" id="object-list" role="tabpanel" aria-labelledby="object-list-tab">
-      {% if table.paginator.num_pages > 1 %}
 
-        {# "Select all" form #}
+      {# Applied filters #}
+      {% if filter_form %}
+        {% applied_filters filter_form request.GET %}
+      {% endif %}
+
+      {# "Select all" form #}
+      {% if table.paginator.num_pages > 1 %}
         {% with bulk_edit_url=content_type.model_class|validated_viewname:"bulk_edit" bulk_delete_url=content_type.model_class|validated_viewname:"bulk_delete" %}
           <div id="select-all-box" class="d-none card noprint">
             <form method="post" class="form col-md-12">

+ 9 - 0
netbox/templates/utilities/templatetags/applied_filters.html

@@ -0,0 +1,9 @@
+{% if applied_filters %}
+  <div class="mb-3">
+    {% for filter in applied_filters %}
+      <a href="{{ filter.link_url }}" class="badge rounded-pill bg-primary text-decoration-none me-1">
+        <i class="mdi mdi-close"></i> {{ filter.link_text }}
+      </a>
+    {% endfor %}
+  </div>
+{% endif %}

+ 26 - 0
netbox/utilities/forms/utils.py

@@ -3,6 +3,7 @@ import re
 from django import forms
 from django.forms.models import fields_for_model
 
+from utilities.choices import unpack_grouped_choices
 from utilities.querysets import RestrictedQuerySet
 from .constants import *
 
@@ -11,6 +12,7 @@ __all__ = (
     'expand_alphanumeric_pattern',
     'expand_ipaddress_pattern',
     'form_from_model',
+    'get_selected_values',
     'parse_alphanumeric_range',
     'parse_numeric_range',
     'restrict_form_fields',
@@ -111,6 +113,30 @@ def expand_ipaddress_pattern(string, family):
             yield ''.join([lead, format(i, 'x' if family == 6 else 'd'), remnant])
 
 
+def get_selected_values(form, field_name):
+    """
+    Return the list of selected human-friendly values for a form field
+    """
+    if not hasattr(form, 'cleaned_data'):
+        form.is_valid()
+
+    # Selection field
+    if hasattr(form.fields[field_name], 'choices'):
+        try:
+            choices = dict(unpack_grouped_choices(form.fields[field_name].choices))
+            return [
+                label for value, label in choices.items() if value in form.cleaned_data[field_name]
+            ]
+        except TypeError:
+            # Field uses dynamic choices. Show all that have been populated.
+            return [
+                subwidget.choice_label for subwidget in form[field_name].subwidgets
+            ]
+
+    # Non-selection field
+    return [str(form.cleaned_data[field_name])]
+
+
 def add_blank_choice(choices):
     """
     Add a blank choice to the beginning of a choices list.

+ 30 - 1
netbox/utilities/templatetags/helpers.py

@@ -13,7 +13,7 @@ from django.utils.html import strip_tags
 from django.utils.safestring import mark_safe
 from markdown import markdown
 
-from utilities.forms import TableConfigForm
+from utilities.forms import get_selected_values, TableConfigForm
 from utilities.utils import foreground_color
 
 register = template.Library()
@@ -403,3 +403,32 @@ def table_config_form(table, table_name=None):
         'table_name': table_name or table.__class__.__name__,
         'table_config_form': TableConfigForm(table=table),
     }
+
+
+@register.inclusion_tag('utilities/templatetags/applied_filters.html')
+def applied_filters(form, query_params):
+    """
+    Display the active filters for a given filter form.
+    """
+    form.is_valid()
+
+    applied_filters = []
+    for filter_name in form.changed_data:
+        if filter_name not in query_params:
+            continue
+
+        bound_field = form.fields[filter_name].get_bound_field(form, filter_name)
+        querydict = query_params.copy()
+        querydict.pop(filter_name)
+        display_value = ', '.join(get_selected_values(form, filter_name))
+
+        applied_filters.append({
+            'name': filter_name,
+            'value': form.cleaned_data[filter_name],
+            'link_url': f'?{querydict.urlencode()}',
+            'link_text': f'{bound_field.label}: {display_value}',
+        })
+
+    return {
+        'applied_filters': applied_filters,
+    }