mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-14 01:41:22 -06:00
Closes #1871: Enable filtering sites by parent region
This commit is contained in:
parent
209a9f0ffc
commit
0a820d9c98
@ -3,6 +3,7 @@ v2.5.3 (FUTURE)
|
|||||||
## Enhancements
|
## Enhancements
|
||||||
|
|
||||||
* [#1630](https://github.com/digitalocean/netbox/issues/1630) - Enable bulk editing of prefix/IP mask length
|
* [#1630](https://github.com/digitalocean/netbox/issues/1630) - Enable bulk editing of prefix/IP mask length
|
||||||
|
* [#1871](https://github.com/digitalocean/netbox/issues/1871) - Enable filtering sites by parent region
|
||||||
* [#2693](https://github.com/digitalocean/netbox/issues/2693) - Additional cable colors
|
* [#2693](https://github.com/digitalocean/netbox/issues/2693) - Additional cable colors
|
||||||
* [#2726](https://github.com/digitalocean/netbox/issues/2726) - Include cables in global search
|
* [#2726](https://github.com/digitalocean/netbox/issues/2726) - Include cables in global search
|
||||||
|
|
||||||
|
@ -62,14 +62,14 @@ class SiteFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
|||||||
choices=SITE_STATUS_CHOICES,
|
choices=SITE_STATUS_CHOICES,
|
||||||
null_value=None
|
null_value=None
|
||||||
)
|
)
|
||||||
region_id = django_filters.ModelMultipleChoiceFilter(
|
region_id = django_filters.NumberFilter(
|
||||||
queryset=Region.objects.all(),
|
method='filter_region',
|
||||||
|
field_name='pk',
|
||||||
label='Region (ID)',
|
label='Region (ID)',
|
||||||
)
|
)
|
||||||
region = django_filters.ModelMultipleChoiceFilter(
|
region = django_filters.CharFilter(
|
||||||
field_name='region__slug',
|
method='filter_region',
|
||||||
queryset=Region.objects.all(),
|
field_name='slug',
|
||||||
to_field_name='slug',
|
|
||||||
label='Region (slug)',
|
label='Region (slug)',
|
||||||
)
|
)
|
||||||
tenant_id = django_filters.ModelMultipleChoiceFilter(
|
tenant_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
@ -108,6 +108,16 @@ class SiteFilter(CustomFieldFilterSet, django_filters.FilterSet):
|
|||||||
pass
|
pass
|
||||||
return queryset.filter(qs_filter)
|
return queryset.filter(qs_filter)
|
||||||
|
|
||||||
|
def filter_region(self, queryset, name, value):
|
||||||
|
try:
|
||||||
|
region = Region.objects.get(**{name: value})
|
||||||
|
except ObjectDoesNotExist:
|
||||||
|
return queryset.none()
|
||||||
|
return queryset.filter(
|
||||||
|
Q(region=region) |
|
||||||
|
Q(region__in=region.get_descendants())
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class RackGroupFilter(django_filters.FilterSet):
|
class RackGroupFilter(django_filters.FilterSet):
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
|
@ -236,9 +236,10 @@ class SiteFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
region = FilterTreeNodeMultipleChoiceField(
|
region = FilterTreeNodeMultipleChoiceField(
|
||||||
queryset=Region.objects.annotate(filter_count=Count('sites')),
|
queryset=Region.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
required=False,
|
required=False,
|
||||||
|
count_attr='site_count'
|
||||||
)
|
)
|
||||||
tenant = FilterChoiceField(
|
tenant = FilterChoiceField(
|
||||||
queryset=Tenant.objects.annotate(filter_count=Count('sites')),
|
queryset=Tenant.objects.annotate(filter_count=Count('sites')),
|
||||||
|
@ -201,6 +201,13 @@ class Region(MPTTModel, ChangeLoggedModel):
|
|||||||
self.parent.name if self.parent else None,
|
self.parent.name if self.parent else None,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def site_count(self):
|
||||||
|
return Site.objects.filter(
|
||||||
|
Q(region=self) |
|
||||||
|
Q(region__in=self.get_descendants())
|
||||||
|
).count()
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Sites
|
# Sites
|
||||||
|
@ -124,7 +124,7 @@ class BulkDisconnectView(GetReturnURLMixin, View):
|
|||||||
#
|
#
|
||||||
|
|
||||||
class RegionListView(ObjectListView):
|
class RegionListView(ObjectListView):
|
||||||
queryset = Region.objects.annotate(site_count=Count('sites'))
|
queryset = Region.objects.all()
|
||||||
filter = filters.RegionFilter
|
filter = filters.RegionFilter
|
||||||
filter_form = forms.RegionFilterForm
|
filter_form = forms.RegionFilterForm
|
||||||
table = tables.RegionTable
|
table = tables.RegionTable
|
||||||
|
@ -505,8 +505,9 @@ class FilterChoiceIterator(forms.models.ModelChoiceIterator):
|
|||||||
class FilterChoiceFieldMixin(object):
|
class FilterChoiceFieldMixin(object):
|
||||||
iterator = FilterChoiceIterator
|
iterator = FilterChoiceIterator
|
||||||
|
|
||||||
def __init__(self, null_label=None, *args, **kwargs):
|
def __init__(self, null_label=None, count_attr='filter_count', *args, **kwargs):
|
||||||
self.null_label = null_label
|
self.null_label = null_label
|
||||||
|
self.count_attr = count_attr
|
||||||
if 'required' not in kwargs:
|
if 'required' not in kwargs:
|
||||||
kwargs['required'] = False
|
kwargs['required'] = False
|
||||||
if 'widget' not in kwargs:
|
if 'widget' not in kwargs:
|
||||||
@ -515,8 +516,9 @@ class FilterChoiceFieldMixin(object):
|
|||||||
|
|
||||||
def label_from_instance(self, obj):
|
def label_from_instance(self, obj):
|
||||||
label = super().label_from_instance(obj)
|
label = super().label_from_instance(obj)
|
||||||
if hasattr(obj, 'filter_count'):
|
obj_count = getattr(obj, self.count_attr, None)
|
||||||
return '{} ({})'.format(label, obj.filter_count)
|
if obj_count is not None:
|
||||||
|
return '{} ({})'.format(label, obj_count)
|
||||||
return label
|
return label
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user