Files
netbox/netbox/extras/filtersets.py
2025-11-25 15:24:02 -06:00

855 lines
26 KiB
Python

import django_filters
from django.contrib.contenttypes.models import ContentType
from django.db.models import Q
from django.utils.translation import gettext as _
from core.models import DataSource, ObjectType
from dcim.models import DeviceRole, DeviceType, Location, Platform, Region, Site, SiteGroup
from netbox.filtersets import BaseFilterSet, ChangeLoggedModelFilterSet, NetBoxModelFilterSet, PrimaryModelFilterSet
from tenancy.models import Tenant, TenantGroup
from users.filterset_mixins import OwnerFilterMixin
from users.models import Group, User
from utilities.filters import (
ContentTypeFilter, MultiValueCharFilter, MultiValueNumberFilter
)
from utilities.filtersets import register_filterset
from virtualization.models import Cluster, ClusterGroup, ClusterType
from .choices import *
from .filters import TagFilter, TagIDFilter
from .models import *
__all__ = (
'BookmarkFilterSet',
'ConfigContextFilterSet',
'ConfigContextProfileFilterSet',
'ConfigTemplateFilterSet',
'CustomFieldChoiceSetFilterSet',
'CustomFieldFilterSet',
'CustomLinkFilterSet',
'EventRuleFilterSet',
'ExportTemplateFilterSet',
'ImageAttachmentFilterSet',
'JournalEntryFilterSet',
'LocalConfigContextFilterSet',
'NotificationGroupFilterSet',
'SavedFilterFilterSet',
'ScriptFilterSet',
'TableConfigFilterSet',
'TagFilterSet',
'TaggedItemFilterSet',
'WebhookFilterSet',
)
@register_filterset
class ScriptFilterSet(BaseFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
)
module_id = django_filters.ModelMultipleChoiceFilter(
queryset=ScriptModule.objects.all(),
label=_('Script module (ID)'),
)
class Meta:
model = Script
fields = ('id', 'name', 'is_executable')
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(
Q(name__icontains=value)
)
@register_filterset
class WebhookFilterSet(OwnerFilterMixin, NetBoxModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
)
http_method = django_filters.MultipleChoiceFilter(
choices=WebhookHttpMethodChoices
)
payload_url = MultiValueCharFilter(
lookup_expr='icontains'
)
class Meta:
model = Webhook
fields = (
'id', 'name', 'payload_url', 'http_method', 'http_content_type', 'secret', 'ssl_verification',
'ca_file_path', 'description',
)
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(
Q(name__icontains=value) |
Q(description__icontains=value) |
Q(payload_url__icontains=value)
)
@register_filterset
class EventRuleFilterSet(OwnerFilterMixin, NetBoxModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
)
object_type_id = django_filters.ModelMultipleChoiceFilter(
queryset=ObjectType.objects.all(),
field_name='object_types'
)
object_type = ContentTypeFilter(
field_name='object_types'
)
event_type = MultiValueCharFilter(
method='filter_event_type'
)
action_type = django_filters.MultipleChoiceFilter(
choices=EventRuleActionChoices
)
action_object_type = ContentTypeFilter()
action_object_id = MultiValueNumberFilter()
class Meta:
model = EventRule
fields = (
'id', 'name', 'enabled', 'action_type', 'description',
)
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(
Q(name__icontains=value) |
Q(description__icontains=value) |
Q(comments__icontains=value)
)
def filter_event_type(self, queryset, name, value):
return queryset.filter(event_types__overlap=value)
@register_filterset
class CustomFieldFilterSet(OwnerFilterMixin, ChangeLoggedModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
)
type = django_filters.MultipleChoiceFilter(
choices=CustomFieldTypeChoices
)
object_type_id = django_filters.ModelMultipleChoiceFilter(
queryset=ObjectType.objects.all(),
field_name='object_types'
)
object_type = ContentTypeFilter(
field_name='object_types'
)
related_object_type_id = django_filters.ModelMultipleChoiceFilter(
queryset=ObjectType.objects.all(),
field_name='related_object_type'
)
related_object_type = ContentTypeFilter()
choice_set_id = django_filters.ModelMultipleChoiceFilter(
queryset=CustomFieldChoiceSet.objects.all()
)
choice_set = django_filters.ModelMultipleChoiceFilter(
field_name='choice_set__name',
queryset=CustomFieldChoiceSet.objects.all(),
to_field_name='name'
)
class Meta:
model = CustomField
fields = (
'id', 'name', 'label', 'group_name', 'required', 'unique', 'search_weight', 'filter_logic', 'ui_visible',
'ui_editable', 'weight', 'is_cloneable', 'description', 'validation_minimum', 'validation_maximum',
'validation_regex',
)
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(
Q(name__icontains=value) |
Q(label__icontains=value) |
Q(group_name__icontains=value) |
Q(description__icontains=value) |
Q(comments__icontains=value)
)
@register_filterset
class CustomFieldChoiceSetFilterSet(OwnerFilterMixin, ChangeLoggedModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
)
choice = MultiValueCharFilter(
method='filter_by_choice'
)
class Meta:
model = CustomFieldChoiceSet
fields = (
'id', 'name', 'description', 'base_choices', 'order_alphabetically',
)
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(
Q(name__icontains=value) |
Q(description__icontains=value)
)
def filter_by_choice(self, queryset, name, value):
# TODO: Support case-insensitive matching
return queryset.filter(extra_choices__overlap=value)
@register_filterset
class CustomLinkFilterSet(OwnerFilterMixin, ChangeLoggedModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
)
object_type_id = django_filters.ModelMultipleChoiceFilter(
queryset=ObjectType.objects.all(),
field_name='object_types'
)
object_type = ContentTypeFilter(
field_name='object_types'
)
class Meta:
model = CustomLink
fields = (
'id', 'name', 'enabled', 'link_text', 'link_url', 'weight', 'group_name', 'new_window', 'button_class',
)
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)
)
@register_filterset
class ExportTemplateFilterSet(OwnerFilterMixin, ChangeLoggedModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
)
object_type_id = django_filters.ModelMultipleChoiceFilter(
queryset=ObjectType.objects.all(),
field_name='object_types'
)
object_type = ContentTypeFilter(
field_name='object_types'
)
data_source_id = django_filters.ModelMultipleChoiceFilter(
queryset=DataSource.objects.all(),
label=_('Data source (ID)'),
)
data_file_id = django_filters.ModelMultipleChoiceFilter(
queryset=DataSource.objects.all(),
label=_('Data file (ID)'),
)
class Meta:
model = ExportTemplate
fields = (
'id', 'name', 'description', 'mime_type', 'file_name', 'file_extension', 'as_attachment',
'auto_sync_enabled', 'data_synced',
)
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(
Q(name__icontains=value) |
Q(description__icontains=value) |
Q(file_name__icontains=value)
)
@register_filterset
class SavedFilterFilterSet(OwnerFilterMixin, ChangeLoggedModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
)
object_type_id = django_filters.ModelMultipleChoiceFilter(
queryset=ObjectType.objects.all(),
field_name='object_types'
)
object_type = ContentTypeFilter(
field_name='object_types'
)
user_id = django_filters.ModelMultipleChoiceFilter(
queryset=User.objects.all(),
label=_('User (ID)'),
)
user = django_filters.ModelMultipleChoiceFilter(
field_name='user__username',
queryset=User.objects.all(),
to_field_name='username',
label=_('User (name)'),
)
usable = django_filters.BooleanFilter(
method='_usable'
)
class Meta:
model = SavedFilter
fields = ('id', 'name', 'slug', 'description', 'enabled', 'shared', 'weight')
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(
Q(name__icontains=value) |
Q(description__icontains=value)
)
def _usable(self, queryset, name, value):
"""
Return only SavedFilters that are both enabled and are shared (or belong to the current user).
"""
user = self.request.user if self.request else None
if not user or user.is_anonymous:
if value:
return queryset.filter(enabled=True, shared=True)
return queryset.filter(Q(enabled=False) | Q(shared=False))
if value:
return queryset.filter(enabled=True).filter(Q(shared=True) | Q(user=user))
return queryset.filter(Q(enabled=False) | Q(Q(shared=False) & ~Q(user=user)))
@register_filterset
class TableConfigFilterSet(ChangeLoggedModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
)
object_type_id = django_filters.ModelMultipleChoiceFilter(
queryset=ObjectType.objects.all(),
field_name='object_type'
)
object_type = ContentTypeFilter(
field_name='object_type'
)
user_id = django_filters.ModelMultipleChoiceFilter(
queryset=User.objects.all(),
label=_('User (ID)'),
)
user = django_filters.ModelMultipleChoiceFilter(
field_name='user__username',
queryset=User.objects.all(),
to_field_name='username',
label=_('User (name)'),
)
usable = django_filters.BooleanFilter(
method='_usable'
)
class Meta:
model = TableConfig
fields = ('id', 'name', 'description', 'table', 'enabled', 'shared', 'weight')
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(
Q(name__icontains=value) |
Q(description__icontains=value) |
Q(table__icontains=value)
)
def _usable(self, queryset, name, value):
"""
Return only TableConfigs that are both enabled and are shared (or belong to the current user).
"""
user = self.request.user if self.request else None
if not user or user.is_anonymous:
if value:
return queryset.filter(enabled=True, shared=True)
return queryset.filter(Q(enabled=False) | Q(shared=False))
if value:
return queryset.filter(enabled=True).filter(Q(shared=True) | Q(user=user))
return queryset.filter(Q(enabled=False) | Q(Q(shared=False) & ~Q(user=user)))
@register_filterset
class BookmarkFilterSet(BaseFilterSet):
created = django_filters.DateTimeFilter()
object_type_id = MultiValueNumberFilter()
object_type = ContentTypeFilter()
user_id = django_filters.ModelMultipleChoiceFilter(
queryset=User.objects.all(),
label=_('User (ID)'),
)
user = django_filters.ModelMultipleChoiceFilter(
field_name='user__username',
queryset=User.objects.all(),
to_field_name='username',
label=_('User (name)'),
)
class Meta:
model = Bookmark
fields = ('id', 'object_id')
@register_filterset
class NotificationGroupFilterSet(ChangeLoggedModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
)
user_id = django_filters.ModelMultipleChoiceFilter(
field_name='users',
queryset=User.objects.all(),
label=_('User (ID)'),
)
user = django_filters.ModelMultipleChoiceFilter(
field_name='users__username',
queryset=User.objects.all(),
to_field_name='username',
label=_('User (name)'),
)
group_id = django_filters.ModelMultipleChoiceFilter(
field_name='groups',
queryset=Group.objects.all(),
label=_('Group (ID)'),
)
group = django_filters.ModelMultipleChoiceFilter(
field_name='groups__name',
queryset=Group.objects.all(),
to_field_name='name',
label=_('Group (name)'),
)
class Meta:
model = NotificationGroup
fields = (
'id', 'name', 'description',
)
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(
Q(name__icontains=value) |
Q(description__icontains=value)
)
@register_filterset
class ImageAttachmentFilterSet(ChangeLoggedModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
)
object_type = ContentTypeFilter()
class Meta:
model = ImageAttachment
fields = ('id', 'object_type_id', 'object_id', 'name', 'description', 'image_width', 'image_height')
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(
Q(name__icontains=value) |
Q(image__icontains=value) |
Q(description__icontains=value)
)
@register_filterset
class JournalEntryFilterSet(NetBoxModelFilterSet):
created = django_filters.DateTimeFromToRangeFilter()
assigned_object_type = ContentTypeFilter()
assigned_object_type_id = django_filters.ModelMultipleChoiceFilter(
queryset=ContentType.objects.all()
)
created_by_id = django_filters.ModelMultipleChoiceFilter(
queryset=User.objects.all(),
label=_('User (ID)'),
)
created_by = django_filters.ModelMultipleChoiceFilter(
field_name='created_by__username',
queryset=User.objects.all(),
to_field_name='username',
label=_('User (name)'),
)
kind = django_filters.MultipleChoiceFilter(
choices=JournalEntryKindChoices
)
class Meta:
model = JournalEntry
fields = ('id', 'assigned_object_type_id', 'assigned_object_id', 'created', 'kind')
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(comments__icontains=value)
@register_filterset
class TagFilterSet(OwnerFilterMixin, ChangeLoggedModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
)
content_type = MultiValueCharFilter(
method='_content_type'
)
content_type_id = MultiValueNumberFilter(
method='_content_type_id'
)
for_object_type_id = MultiValueNumberFilter(
method='_for_object_type'
)
class Meta:
model = Tag
fields = ('id', 'name', 'slug', 'color', 'weight', 'description', 'object_types')
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)
)
def _content_type(self, queryset, name, values):
ct_filter = Q()
# Compile list of app_label & model pairings
for value in values:
try:
app_label, model = value.lower().split('.')
ct_filter |= Q(
app_label=app_label,
model=model
)
except ValueError:
pass
# Get ContentType instances
content_types = ContentType.objects.filter(ct_filter)
return queryset.filter(extras_taggeditem_items__content_type__in=content_types).distinct()
def _content_type_id(self, queryset, name, values):
# Get ContentType instances
content_types = ContentType.objects.filter(pk__in=values)
return queryset.filter(extras_taggeditem_items__content_type__in=content_types).distinct()
def _for_object_type(self, queryset, name, values):
return queryset.filter(
Q(object_types__id__in=values) | Q(object_types__isnull=True)
)
@register_filterset
class TaggedItemFilterSet(BaseFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
)
object_type = ContentTypeFilter(
field_name='content_type'
)
object_type_id = django_filters.ModelMultipleChoiceFilter(
queryset=ContentType.objects.all(),
field_name='content_type_id'
)
tag_id = django_filters.ModelMultipleChoiceFilter(
queryset=Tag.objects.all()
)
tag = django_filters.ModelMultipleChoiceFilter(
field_name='tag__slug',
queryset=Tag.objects.all(),
to_field_name='slug',
)
class Meta:
model = TaggedItem
fields = ('id', 'object_id')
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(
Q(tag__name__icontains=value) |
Q(tag__slug__icontains=value) |
Q(tag__description__icontains=value)
)
@register_filterset
class ConfigContextProfileFilterSet(PrimaryModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
)
data_source_id = django_filters.ModelMultipleChoiceFilter(
queryset=DataSource.objects.all(),
label=_('Data source (ID)'),
)
data_file_id = django_filters.ModelMultipleChoiceFilter(
queryset=DataSource.objects.all(),
label=_('Data file (ID)'),
)
class Meta:
model = ConfigContextProfile
fields = (
'id', 'name', 'description', 'auto_sync_enabled', 'data_synced',
)
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(
Q(name__icontains=value) |
Q(description__icontains=value) |
Q(comments__icontains=value)
)
@register_filterset
class ConfigContextFilterSet(OwnerFilterMixin, ChangeLoggedModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
)
profile_id = django_filters.ModelMultipleChoiceFilter(
queryset=ConfigContextProfile.objects.all(),
label=_('Profile (ID)'),
)
profile = django_filters.ModelMultipleChoiceFilter(
field_name='profile__name',
queryset=ConfigContextProfile.objects.all(),
to_field_name='name',
label=_('Profile (name)'),
)
region_id = django_filters.ModelMultipleChoiceFilter(
field_name='regions',
queryset=Region.objects.all(),
label=_('Region'),
)
region = django_filters.ModelMultipleChoiceFilter(
field_name='regions__slug',
queryset=Region.objects.all(),
to_field_name='slug',
label=_('Region (slug)'),
)
site_group = django_filters.ModelMultipleChoiceFilter(
field_name='site_groups__slug',
queryset=SiteGroup.objects.all(),
to_field_name='slug',
label=_('Site group (slug)'),
)
site_group_id = django_filters.ModelMultipleChoiceFilter(
field_name='site_groups',
queryset=SiteGroup.objects.all(),
label=_('Site group'),
)
site_id = django_filters.ModelMultipleChoiceFilter(
field_name='sites',
queryset=Site.objects.all(),
label=_('Site'),
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='sites__slug',
queryset=Site.objects.all(),
to_field_name='slug',
label=_('Site (slug)'),
)
location_id = django_filters.ModelMultipleChoiceFilter(
field_name='locations',
queryset=Location.objects.all(),
label=_('Location'),
)
location = django_filters.ModelMultipleChoiceFilter(
field_name='locations__slug',
queryset=Location.objects.all(),
to_field_name='slug',
label=_('Location (slug)'),
)
device_type_id = django_filters.ModelMultipleChoiceFilter(
field_name='device_types',
queryset=DeviceType.objects.all(),
label=_('Device type'),
)
device_role_id = django_filters.ModelMultipleChoiceFilter(
field_name='roles',
queryset=DeviceRole.objects.all(),
label=_('Role'),
)
device_role = django_filters.ModelMultipleChoiceFilter(
field_name='roles__slug',
queryset=DeviceRole.objects.all(),
to_field_name='slug',
label=_('Role (slug)'),
)
platform_id = django_filters.ModelMultipleChoiceFilter(
field_name='platforms',
queryset=Platform.objects.all(),
label=_('Platform'),
)
platform = django_filters.ModelMultipleChoiceFilter(
field_name='platforms__slug',
queryset=Platform.objects.all(),
to_field_name='slug',
label=_('Platform (slug)'),
)
cluster_type_id = django_filters.ModelMultipleChoiceFilter(
field_name='cluster_types',
queryset=ClusterType.objects.all(),
label=_('Cluster type'),
)
cluster_type = django_filters.ModelMultipleChoiceFilter(
field_name='cluster_types__slug',
queryset=ClusterType.objects.all(),
to_field_name='slug',
label=_('Cluster type (slug)'),
)
cluster_group_id = django_filters.ModelMultipleChoiceFilter(
field_name='cluster_groups',
queryset=ClusterGroup.objects.all(),
label=_('Cluster group'),
)
cluster_group = django_filters.ModelMultipleChoiceFilter(
field_name='cluster_groups__slug',
queryset=ClusterGroup.objects.all(),
to_field_name='slug',
label=_('Cluster group (slug)'),
)
cluster_id = django_filters.ModelMultipleChoiceFilter(
field_name='clusters',
queryset=Cluster.objects.all(),
label=_('Cluster'),
)
tenant_group_id = django_filters.ModelMultipleChoiceFilter(
field_name='tenant_groups',
queryset=TenantGroup.objects.all(),
label=_('Tenant group'),
)
tenant_group = django_filters.ModelMultipleChoiceFilter(
field_name='tenant_groups__slug',
queryset=TenantGroup.objects.all(),
to_field_name='slug',
label=_('Tenant group (slug)'),
)
tenant_id = django_filters.ModelMultipleChoiceFilter(
field_name='tenants',
queryset=Tenant.objects.all(),
label=_('Tenant'),
)
tenant = django_filters.ModelMultipleChoiceFilter(
field_name='tenants__slug',
queryset=Tenant.objects.all(),
to_field_name='slug',
label=_('Tenant (slug)'),
)
tag_id = django_filters.ModelMultipleChoiceFilter(
field_name='tags',
queryset=Tag.objects.all(),
label=_('Tag'),
)
tag = django_filters.ModelMultipleChoiceFilter(
field_name='tags__slug',
queryset=Tag.objects.all(),
to_field_name='slug',
label=_('Tag (slug)'),
)
data_source_id = django_filters.ModelMultipleChoiceFilter(
queryset=DataSource.objects.all(),
label=_('Data source (ID)'),
)
data_file_id = django_filters.ModelMultipleChoiceFilter(
queryset=DataSource.objects.all(),
label=_('Data file (ID)'),
)
class Meta:
model = ConfigContext
fields = ('id', 'name', 'is_active', 'description', 'weight', 'auto_sync_enabled', 'data_synced')
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(
Q(name__icontains=value) |
Q(description__icontains=value) |
Q(data__icontains=value)
)
@register_filterset
class ConfigTemplateFilterSet(OwnerFilterMixin, ChangeLoggedModelFilterSet):
q = django_filters.CharFilter(
method='search',
label=_('Search'),
)
data_source_id = django_filters.ModelMultipleChoiceFilter(
queryset=DataSource.objects.all(),
label=_('Data source (ID)'),
)
data_file_id = django_filters.ModelMultipleChoiceFilter(
queryset=DataSource.objects.all(),
label=_('Data file (ID)'),
)
tag = TagFilter()
tag_id = TagIDFilter()
class Meta:
model = ConfigTemplate
fields = (
'id', 'name', 'description', 'mime_type', 'file_name', 'file_extension', 'as_attachment',
'auto_sync_enabled', 'data_synced'
)
def search(self, queryset, name, value):
if not value.strip():
return queryset
return queryset.filter(
Q(name__icontains=value) |
Q(description__icontains=value)
)
#
# Filter for Local Config Context Data
#
class LocalConfigContextFilterSet(django_filters.FilterSet):
local_context_data = django_filters.BooleanFilter(
method='_local_context_data',
label=_('Has local config context data'),
)
def _local_context_data(self, queryset, name, value):
return queryset.exclude(local_context_data__isnull=value)