Extract base NestedGroupModelFilterSet with base search behavior

This can easily be extended (as in the case of LocationFilterSet) by
calling super() and ORing a filter to the queryset that is returned.
See: https://docs.djangoproject.com/en/5.1/ref/models/querysets/#or
This commit is contained in:
Jason Novinger 2025-03-13 15:36:55 -05:00
parent 2df68e29c9
commit 06a206ee33
4 changed files with 33 additions and 68 deletions

View File

@ -11,7 +11,8 @@ from ipam.filtersets import PrimaryIPFilterSet
from ipam.models import ASN, IPAddress, VLANTranslationPolicy, VRF
from netbox.choices import ColorChoices
from netbox.filtersets import (
BaseFilterSet, ChangeLoggedModelFilterSet, OrganizationalModelFilterSet, NetBoxModelFilterSet,
BaseFilterSet, ChangeLoggedModelFilterSet, NestedGroupModelFilterSet, NetBoxModelFilterSet,
OrganizationalModelFilterSet,
)
from tenancy.filtersets import TenancyFilterSet, ContactModelFilterSet
from tenancy.models import *
@ -81,7 +82,7 @@ __all__ = (
)
class RegionFilterSet(OrganizationalModelFilterSet, ContactModelFilterSet):
class RegionFilterSet(NestedGroupModelFilterSet, ContactModelFilterSet):
parent_id = django_filters.ModelMultipleChoiceFilter(
queryset=Region.objects.all(),
label=_('Parent region (ID)'),
@ -110,18 +111,8 @@ class RegionFilterSet(OrganizationalModelFilterSet, ContactModelFilterSet):
model = Region
fields = ('id', 'name', 'slug', 'description')
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(
Q(name__icontains=value) |
Q(slug__icontains=value) |
Q(description__icontains=value) |
Q(comments__icontains=value)
).distinct()
class SiteGroupFilterSet(OrganizationalModelFilterSet, ContactModelFilterSet):
class SiteGroupFilterSet(NestedGroupModelFilterSet, ContactModelFilterSet):
parent_id = django_filters.ModelMultipleChoiceFilter(
queryset=SiteGroup.objects.all(),
label=_('Parent site group (ID)'),
@ -150,16 +141,6 @@ class SiteGroupFilterSet(OrganizationalModelFilterSet, ContactModelFilterSet):
model = SiteGroup
fields = ('id', 'name', 'slug', 'description')
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(
Q(name__icontains=value) |
Q(slug__icontains=value) |
Q(description__icontains=value) |
Q(comments__icontains=value)
).distinct()
class SiteFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
status = django_filters.MultipleChoiceFilter(
@ -225,7 +206,7 @@ class SiteFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSe
return queryset.filter(qs_filter).distinct()
class LocationFilterSet(TenancyFilterSet, ContactModelFilterSet, OrganizationalModelFilterSet):
class LocationFilterSet(TenancyFilterSet, ContactModelFilterSet, NestedGroupModelFilterSet):
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region',
@ -295,15 +276,13 @@ class LocationFilterSet(TenancyFilterSet, ContactModelFilterSet, OrganizationalM
fields = ('id', 'name', 'slug', 'facility', 'description')
def search(self, queryset, name, value):
if not value.strip():
# extended in order to include querying on Location.facility
queryset = super().search(queryset, name, value)
if value.strip():
queryset = queryset | queryset.model.objects.filter(facility__icontains=value)
return queryset
return queryset.filter(
Q(name__icontains=value) |
Q(slug__icontains=value) |
Q(facility__icontains=value) |
Q(description__icontains=value) |
Q(comments__icontains=value)
)
class RackRoleFilterSet(OrganizationalModelFilterSet):

View File

@ -329,3 +329,19 @@ class OrganizationalModelFilterSet(NetBoxModelFilterSet):
models.Q(slug__icontains=value) |
models.Q(description__icontains=value)
)
class NestedGroupModelFilterSet(NetBoxModelFilterSet):
"""
A base FilterSet for models that inherit from NestedGroupModel
"""
def search(self, queryset, name, value):
if value.strip():
queryset = queryset.filter(
models.Q(name__icontains=value) |
models.Q(slug__icontains=value) |
models.Q(description__icontains=value) |
models.Q(comments__icontains=value)
)
return queryset

View File

@ -2,7 +2,7 @@ import django_filters
from django.db.models import Q
from django.utils.translation import gettext as _
from netbox.filtersets import NetBoxModelFilterSet, OrganizationalModelFilterSet
from netbox.filtersets import NestedGroupModelFilterSet, NetBoxModelFilterSet, OrganizationalModelFilterSet
from utilities.filters import ContentTypeFilter, TreeNodeMultipleChoiceFilter
from .models import *
@ -22,7 +22,7 @@ __all__ = (
# Contacts
#
class ContactGroupFilterSet(OrganizationalModelFilterSet):
class ContactGroupFilterSet(NestedGroupModelFilterSet):
parent_id = django_filters.ModelMultipleChoiceFilter(
queryset=ContactGroup.objects.all(),
label=_('Parent contact group (ID)'),
@ -51,16 +51,6 @@ class ContactGroupFilterSet(OrganizationalModelFilterSet):
model = ContactGroup
fields = ('id', 'name', 'slug', 'description')
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(
Q(name__icontains=value) |
Q(slug__icontains=value) |
Q(description__icontains=value) |
Q(comments__icontains=value)
)
class ContactRoleFilterSet(OrganizationalModelFilterSet):
@ -173,7 +163,7 @@ class ContactModelFilterSet(django_filters.FilterSet):
# Tenancy
#
class TenantGroupFilterSet(OrganizationalModelFilterSet):
class TenantGroupFilterSet(NestedGroupModelFilterSet):
parent_id = django_filters.ModelMultipleChoiceFilter(
queryset=TenantGroup.objects.all(),
label=_('Parent tenant group (ID)'),
@ -202,16 +192,6 @@ class TenantGroupFilterSet(OrganizationalModelFilterSet):
model = TenantGroup
fields = ('id', 'name', 'slug', 'description')
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(
Q(name__icontains=value) |
Q(slug__icontains=value) |
Q(description__icontains=value) |
Q(comments__icontains=value)
)
class TenantFilterSet(NetBoxModelFilterSet, ContactModelFilterSet):
group_id = TreeNodeMultipleChoiceFilter(

View File

@ -5,7 +5,7 @@ from dcim.choices import LinkStatusChoices
from dcim.base_filtersets import ScopedFilterSet
from dcim.models import Interface
from ipam.models import VLAN
from netbox.filtersets import OrganizationalModelFilterSet, NetBoxModelFilterSet
from netbox.filtersets import NestedGroupModelFilterSet, NetBoxModelFilterSet
from tenancy.filtersets import TenancyFilterSet
from utilities.filters import TreeNodeMultipleChoiceFilter
from .choices import *
@ -18,7 +18,7 @@ __all__ = (
)
class WirelessLANGroupFilterSet(OrganizationalModelFilterSet):
class WirelessLANGroupFilterSet(NestedGroupModelFilterSet):
parent_id = django_filters.ModelMultipleChoiceFilter(
queryset=WirelessLANGroup.objects.all()
)
@ -43,16 +43,6 @@ class WirelessLANGroupFilterSet(OrganizationalModelFilterSet):
model = WirelessLANGroup
fields = ('id', 'name', 'slug', 'description')
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(
Q(name__icontains=value) |
Q(slug__icontains=value) |
Q(description__icontains=value) |
Q(comments__icontains=value)
)
class WirelessLANFilterSet(NetBoxModelFilterSet, ScopedFilterSet, TenancyFilterSet):
group_id = TreeNodeMultipleChoiceFilter(