1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Merge branch 'develop' into develop-2.7

This commit is contained in:
Jeremy Stretch
2020-01-03 14:21:53 -05:00
10 changed files with 333 additions and 40 deletions

View File

@ -1,3 +1,12 @@
# v2.6.11 (2020-01-03)
## Bug Fixes
* [#3831](https://github.com/netbox-community/netbox/issues/3831) - Fix API-driven filter field rendering (#3812 regression)
* [#3833](https://github.com/netbox-community/netbox/issues/3833) - Add missing region filters for multiple objects
---
# v2.6.10 (2020-01-02)
## Enhancements

View File

@ -18,6 +18,17 @@ class ProviderFilter(CustomFieldFilterSet, CreatedUpdatedFilterSet):
method='search',
label='Search',
)
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='circuits__terminations__site__region__in',
label='Region (ID)',
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='circuits__terminations__site__region__in',
to_field_name='slug',
label='Region (slug)',
)
site_id = django_filters.ModelMultipleChoiceFilter(
field_name='circuits__terminations__site',
queryset=Site.objects.all(),

View File

@ -104,6 +104,18 @@ class ProviderFilterForm(BootstrapMixin, CustomFieldFilterForm):
required=False,
label='Search'
)
region = FilterChoiceField(
queryset=Region.objects.all(),
to_field_name='slug',
required=False,
widget=APISelectMultiple(
api_url="/api/dcim/regions/",
value_field="slug",
filter_for={
'site': 'region'
}
)
)
site = FilterChoiceField(
queryset=Site.objects.all(),
to_field_name='slug',
@ -302,6 +314,9 @@ class CircuitFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm
widget=APISelectMultiple(
api_url="/api/dcim/regions/",
value_field="slug",
filter_for={
'site': 'region'
}
)
)
site = FilterChoiceField(

View File

@ -94,6 +94,17 @@ class SiteFilter(TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet
class RackGroupFilter(NameSlugSearchFilterSet):
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region__in',
label='Region (ID)',
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region__in',
to_field_name='slug',
label='Region (slug)',
)
site_id = django_filters.ModelMultipleChoiceFilter(
queryset=Site.objects.all(),
label='Site (ID)',
@ -126,6 +137,17 @@ class RackFilter(TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet
method='search',
label='Search',
)
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region__in',
label='Region (ID)',
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region__in',
to_field_name='slug',
label='Region (slug)',
)
site_id = django_filters.ModelMultipleChoiceFilter(
queryset=Site.objects.all(),
label='Site (ID)',
@ -889,6 +911,28 @@ class InventoryItemFilter(DeviceComponentFilterSet):
method='search',
label='Search',
)
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='device__site__region__in',
label='Region (ID)',
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='device__site__region__in',
to_field_name='slug',
label='Region (slug)',
)
site_id = django_filters.ModelMultipleChoiceFilter(
field_name='device__site',
queryset=Site.objects.all(),
label='Site (ID)',
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='device__site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
label='Site name (slug)',
)
device_id = django_filters.ModelChoiceFilter(
queryset=Device.objects.all(),
label='Device (ID)',
@ -938,6 +982,17 @@ class VirtualChassisFilter(django_filters.FilterSet):
method='search',
label='Search',
)
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='master__site__region__in',
label='Region (ID)',
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='master__site__region__in',
to_field_name='slug',
label='Region (slug)',
)
site_id = django_filters.ModelMultipleChoiceFilter(
field_name='master__site',
queryset=Site.objects.all(),
@ -1136,6 +1191,17 @@ class PowerPanelFilter(django_filters.FilterSet):
method='search',
label='Search',
)
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region__in',
label='Region (ID)',
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region__in',
to_field_name='slug',
label='Region (slug)',
)
site_id = django_filters.ModelMultipleChoiceFilter(
queryset=Site.objects.all(),
label='Site (ID)',
@ -1174,6 +1240,17 @@ class PowerFeedFilter(CustomFieldFilterSet, CreatedUpdatedFilterSet):
method='search',
label='Search',
)
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='power_panel__site__region__in',
label='Region (ID)',
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='power_panel__site__region__in',
to_field_name='slug',
label='Region (slug)',
)
site_id = django_filters.ModelMultipleChoiceFilter(
field_name='power_panel__site',
queryset=Site.objects.all(),

View File

@ -392,6 +392,18 @@ class RackGroupCSVForm(forms.ModelForm):
class RackGroupFilterForm(BootstrapMixin, forms.Form):
region = FilterChoiceField(
queryset=Region.objects.all(),
to_field_name='slug',
required=False,
widget=APISelectMultiple(
api_url="/api/dcim/regions/",
value_field="slug",
filter_for={
'site': 'region'
}
)
)
site = FilterChoiceField(
queryset=Site.objects.all(),
to_field_name='slug',
@ -660,11 +672,23 @@ class RackBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditFor
class RackFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
model = Rack
field_order = ['q', 'site', 'group_id', 'status', 'role', 'tenant_group', 'tenant']
field_order = ['q', 'region', 'site', 'group_id', 'status', 'role', 'tenant_group', 'tenant']
q = forms.CharField(
required=False,
label='Search'
)
region = FilterChoiceField(
queryset=Region.objects.all(),
to_field_name='slug',
required=False,
widget=APISelectMultiple(
api_url="/api/dcim/regions/",
value_field="slug",
filter_for={
'site': 'region'
}
)
)
site = FilterChoiceField(
queryset=Site.objects.all(),
to_field_name='slug',
@ -676,16 +700,15 @@ class RackFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
}
)
)
group_id = ChainedModelChoiceField(
label='Rack group',
queryset=RackGroup.objects.prefetch_related('site'),
chains=(
('site', 'site'),
group_id = FilterChoiceField(
queryset=RackGroup.objects.prefetch_related(
'site'
),
required=False,
label='Rack group',
null_label='-- None --',
widget=APISelectMultiple(
api_url="/api/dcim/rack-groups/",
null_option=True,
null_option=True
)
)
status = forms.MultipleChoiceField(
@ -3879,6 +3902,29 @@ class InventoryItemFilterForm(BootstrapMixin, forms.Form):
required=False,
label='Search'
)
region = FilterChoiceField(
queryset=Region.objects.all(),
to_field_name='slug',
required=False,
widget=APISelectMultiple(
api_url="/api/dcim/regions/",
value_field="slug",
filter_for={
'site': 'region'
}
)
)
site = FilterChoiceField(
queryset=Site.objects.all(),
to_field_name='slug',
widget=APISelectMultiple(
api_url="/api/dcim/sites/",
value_field="slug",
filter_for={
'device_id': 'site'
}
)
)
device_id = FilterChoiceField(
queryset=Device.objects.all(),
required=False,
@ -4044,6 +4090,18 @@ class VirtualChassisFilterForm(BootstrapMixin, CustomFieldFilterForm):
required=False,
label='Search'
)
region = FilterChoiceField(
queryset=Region.objects.all(),
to_field_name='slug',
required=False,
widget=APISelectMultiple(
api_url="/api/dcim/regions/",
value_field="slug",
filter_for={
'site': 'region'
}
)
)
site = FilterChoiceField(
queryset=Site.objects.all(),
to_field_name='slug',
@ -4149,6 +4207,18 @@ class PowerPanelFilterForm(BootstrapMixin, CustomFieldFilterForm):
required=False,
label='Search'
)
region = FilterChoiceField(
queryset=Region.objects.all(),
to_field_name='slug',
required=False,
widget=APISelectMultiple(
api_url="/api/dcim/regions/",
value_field="slug",
filter_for={
'site': 'region'
}
)
)
site = FilterChoiceField(
queryset=Site.objects.all(),
to_field_name='slug',
@ -4369,6 +4439,18 @@ class PowerFeedFilterForm(BootstrapMixin, CustomFieldFilterForm):
required=False,
label='Search'
)
region = FilterChoiceField(
queryset=Region.objects.all(),
to_field_name='slug',
required=False,
widget=APISelectMultiple(
api_url="/api/dcim/regions/",
value_field="slug",
filter_for={
'site': 'region'
}
)
)
site = FilterChoiceField(
queryset=Site.objects.all(),
to_field_name='slug',

View File

@ -4,10 +4,10 @@ from django.core.exceptions import ValidationError
from django.db.models import Q
from netaddr.core import AddrFormatError
from dcim.models import Site, Device, Interface
from dcim.models import Device, Interface, Region, Site
from extras.filters import CustomFieldFilterSet, CreatedUpdatedFilterSet
from tenancy.filtersets import TenancyFilterSet
from utilities.filters import NameSlugSearchFilterSet, NumericInFilter, TagFilter
from utilities.filters import NameSlugSearchFilterSet, NumericInFilter, TagFilter, TreeNodeMultipleChoiceFilter
from virtualization.models import VirtualMachine
from .choices import *
from .models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
@ -149,6 +149,17 @@ class PrefixFilter(TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterS
to_field_name='rd',
label='VRF (RD)',
)
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region__in',
label='Region (ID)',
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region__in',
to_field_name='slug',
label='Region (slug)',
)
site_id = django_filters.ModelMultipleChoiceFilter(
queryset=Site.objects.all(),
label='Site (ID)',
@ -375,6 +386,17 @@ class IPAddressFilter(TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilt
class VLANGroupFilter(NameSlugSearchFilterSet):
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region__in',
label='Region (ID)',
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region__in',
to_field_name='slug',
label='Region (slug)',
)
site_id = django_filters.ModelMultipleChoiceFilter(
queryset=Site.objects.all(),
label='Site (ID)',
@ -400,6 +422,17 @@ class VLANFilter(TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet
method='search',
label='Search',
)
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region__in',
label='Region (ID)',
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region__in',
to_field_name='slug',
label='Region (slug)',
)
site_id = django_filters.ModelMultipleChoiceFilter(
queryset=Site.objects.all(),
label='Site (ID)',

View File

@ -3,7 +3,7 @@ from django.core.exceptions import MultipleObjectsReturned
from django.core.validators import MaxValueValidator, MinValueValidator
from taggit.forms import TagField
from dcim.models import Site, Rack, Device, Interface
from dcim.models import Device, Interface, Rack, Region, Site
from extras.forms import AddRemoveTagsForm, CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFilterForm
from tenancy.forms import TenancyFilterForm, TenancyForm
from tenancy.models import Tenant
@ -492,8 +492,8 @@ class PrefixBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditF
class PrefixFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
model = Prefix
field_order = [
'q', 'within_include', 'family', 'mask_length', 'vrf_id', 'status', 'site', 'role', 'tenant_group', 'tenant',
'is_pool', 'expand',
'q', 'within_include', 'family', 'mask_length', 'vrf_id', 'status', 'region', 'site', 'role', 'tenant_group',
'tenant', 'is_pool', 'expand',
]
q = forms.CharField(
required=False,
@ -534,6 +534,18 @@ class PrefixFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm)
required=False,
widget=StaticSelect2Multiple()
)
region = FilterChoiceField(
queryset=Region.objects.all(),
to_field_name='slug',
required=False,
widget=APISelectMultiple(
api_url="/api/dcim/regions/",
value_field="slug",
filter_for={
'site': 'region'
}
)
)
site = FilterChoiceField(
queryset=Site.objects.all(),
to_field_name='slug',
@ -1034,6 +1046,18 @@ class VLANGroupCSVForm(forms.ModelForm):
class VLANGroupFilterForm(BootstrapMixin, forms.Form):
region = FilterChoiceField(
queryset=Region.objects.all(),
to_field_name='slug',
required=False,
widget=APISelectMultiple(
api_url="/api/dcim/regions/",
value_field="slug",
filter_for={
'site': 'region',
}
)
)
site = FilterChoiceField(
queryset=Site.objects.all(),
to_field_name='slug',
@ -1215,11 +1239,24 @@ class VLANBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditFor
class VLANFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
model = VLAN
field_order = ['q', 'site', 'group_id', 'status', 'role', 'tenant_group', 'tenant']
field_order = ['q', 'region', 'site', 'group_id', 'status', 'role', 'tenant_group', 'tenant']
q = forms.CharField(
required=False,
label='Search'
)
region = FilterChoiceField(
queryset=Region.objects.all(),
to_field_name='slug',
required=False,
widget=APISelectMultiple(
api_url="/api/dcim/regions/",
value_field="slug",
filter_for={
'site': 'region',
'group_id': 'region'
}
)
)
site = FilterChoiceField(
queryset=Site.objects.all(),
to_field_name='slug',

View File

@ -1,5 +1,9 @@
<select name="{{ widget.name }}"{% include "django/forms/widgets/attrs.html" %}>{% for group_name, group_choices, group_index in widget.optgroups %}{% if group_name %}
<optgroup label="{{ group_name }}">{% endif %}{% for widget in group_choices %}{% if widget.attrs.selected %}
{% include widget.template_name %}{% endif %}{% endfor %}{% if group_name %}
</optgroup>{% endif %}{% endfor %}
<select name="{{ widget.name }}"{% include "django/forms/widgets/attrs.html" %}>
{% for group_name, group_choices, group_index in widget.optgroups %}
{% if group_name %}<optgroup label="{{ group_name }}">{% endif %}
{% for option in group_choices %}
{% if option.attrs.selected or option.value == "null" %}{% include option.template_name with widget=option %}{% endif %}
{% endfor %}
{% if group_name %}</optgroup>{% endif %}
{% endfor %}
</select>

View File

@ -37,6 +37,27 @@ class ClusterFilter(CustomFieldFilterSet, CreatedUpdatedFilterSet):
method='search',
label='Search',
)
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region__in',
label='Region (ID)',
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region__in',
to_field_name='slug',
label='Region (slug)',
)
site_id = django_filters.ModelMultipleChoiceFilter(
queryset=Site.objects.all(),
label='Site (ID)',
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
label='Site (slug)',
)
group_id = django_filters.ModelMultipleChoiceFilter(
queryset=ClusterGroup.objects.all(),
label='Parent group (ID)',
@ -61,16 +82,6 @@ class ClusterFilter(CustomFieldFilterSet, CreatedUpdatedFilterSet):
queryset=Tenant.objects.all(),
label="Tenant (ID)"
)
site_id = django_filters.ModelMultipleChoiceFilter(
queryset=Site.objects.all(),
label='Site (ID)',
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
label='Site (slug)',
)
tag = TagFilter()
class Meta:

View File

@ -186,6 +186,29 @@ class ClusterBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEdit
class ClusterFilterForm(BootstrapMixin, CustomFieldFilterForm):
model = Cluster
q = forms.CharField(required=False, label='Search')
region = FilterChoiceField(
queryset=Region.objects.all(),
to_field_name='slug',
required=False,
widget=APISelectMultiple(
api_url="/api/dcim/regions/",
value_field="slug",
filter_for={
'site': 'region'
}
)
)
site = FilterChoiceField(
queryset=Site.objects.all(),
to_field_name='slug',
null_label='-- None --',
required=False,
widget=APISelectMultiple(
api_url="/api/dcim/sites/",
value_field='slug',
null_option=True,
)
)
type = FilterChoiceField(
queryset=ClusterType.objects.all(),
to_field_name='slug',
@ -215,17 +238,6 @@ class ClusterFilterForm(BootstrapMixin, CustomFieldFilterForm):
null_option=True,
)
)
site = FilterChoiceField(
queryset=Site.objects.all(),
to_field_name='slug',
null_label='-- None --',
required=False,
widget=APISelectMultiple(
api_url="/api/dcim/sites/",
value_field='slug',
null_option=True,
)
)
class ClusterAddDevicesForm(BootstrapMixin, ChainedFieldsMixin, forms.Form):
@ -585,7 +597,9 @@ class VirtualMachineFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFil
widget=APISelectMultiple(
api_url='/api/dcim/regions/',
value_field="slug",
null_option=True,
filter_for={
'site': 'region'
}
)
)
site = FilterChoiceField(