Merge pull request #8742 from minitriga/issue_8645

Allow filtering on Core models for Contacts
This commit is contained in:
Jeremy Stretch 2022-03-18 11:24:09 -04:00 committed by GitHub
commit 1541060091
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 238 additions and 139 deletions

View File

@ -5,7 +5,7 @@ from dcim.filtersets import CableTerminationFilterSet
from dcim.models import Region, Site, SiteGroup from dcim.models import Region, Site, SiteGroup
from extras.filters import TagFilter from extras.filters import TagFilter
from netbox.filtersets import ChangeLoggedModelFilterSet, OrganizationalModelFilterSet, PrimaryModelFilterSet from netbox.filtersets import ChangeLoggedModelFilterSet, OrganizationalModelFilterSet, PrimaryModelFilterSet
from tenancy.filtersets import TenancyFilterSet from tenancy.filtersets import ContactModelFilterSet, TenancyFilterSet
from utilities.filters import TreeNodeMultipleChoiceFilter from utilities.filters import TreeNodeMultipleChoiceFilter
from .choices import * from .choices import *
from .models import * from .models import *
@ -19,7 +19,7 @@ __all__ = (
) )
class ProviderFilterSet(PrimaryModelFilterSet): class ProviderFilterSet(PrimaryModelFilterSet, ContactModelFilterSet):
q = django_filters.CharFilter( q = django_filters.CharFilter(
method='search', method='search',
label='Search', label='Search',
@ -118,7 +118,7 @@ class CircuitTypeFilterSet(OrganizationalModelFilterSet):
fields = ['id', 'name', 'slug', 'description'] fields = ['id', 'name', 'slug', 'description']
class CircuitFilterSet(PrimaryModelFilterSet, TenancyFilterSet): class CircuitFilterSet(PrimaryModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
q = django_filters.CharFilter( q = django_filters.CharFilter(
method='search', method='search',
label='Search', label='Search',

View File

@ -5,7 +5,7 @@ from circuits.choices import CircuitStatusChoices
from circuits.models import * from circuits.models import *
from dcim.models import Region, Site, SiteGroup from dcim.models import Region, Site, SiteGroup
from extras.forms import CustomFieldModelFilterForm from extras.forms import CustomFieldModelFilterForm
from tenancy.forms import TenancyFilterForm from tenancy.forms import TenancyFilterForm, ContactModelFilterForm
from utilities.forms import DynamicModelMultipleChoiceField, StaticSelectMultiple, TagFilterField from utilities.forms import DynamicModelMultipleChoiceField, StaticSelectMultiple, TagFilterField
__all__ = ( __all__ = (
@ -16,12 +16,13 @@ __all__ = (
) )
class ProviderFilterForm(CustomFieldModelFilterForm): class ProviderFilterForm(ContactModelFilterForm, CustomFieldModelFilterForm):
model = Provider model = Provider
field_groups = [ field_groups = [
['q', 'tag'], ['q', 'tag'],
['region_id', 'site_group_id', 'site_id'], ['region_id', 'site_group_id', 'site_id'],
['asn'], ['asn'],
['contact', 'contact_role']
] ]
region_id = DynamicModelMultipleChoiceField( region_id = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(), queryset=Region.objects.all(),
@ -68,7 +69,7 @@ class CircuitTypeFilterForm(CustomFieldModelFilterForm):
tag = TagFilterField(model) tag = TagFilterField(model)
class CircuitFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): class CircuitFilterForm(TenancyFilterForm, ContactModelFilterForm, CustomFieldModelFilterForm):
model = Circuit model = Circuit
field_groups = [ field_groups = [
['q', 'tag'], ['q', 'tag'],
@ -76,6 +77,7 @@ class CircuitFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
['type_id', 'status', 'commit_rate'], ['type_id', 'status', 'commit_rate'],
['region_id', 'site_group_id', 'site_id'], ['region_id', 'site_group_id', 'site_id'],
['tenant_group_id', 'tenant_id'], ['tenant_group_id', 'tenant_id'],
['contact', 'contact_role']
] ]
type_id = DynamicModelMultipleChoiceField( type_id = DynamicModelMultipleChoiceField(
queryset=CircuitType.objects.all(), queryset=CircuitType.objects.all(),

View File

@ -58,6 +58,9 @@ class ProviderTable(BaseTable):
verbose_name='Circuits' verbose_name='Circuits'
) )
comments = MarkdownColumn() comments = MarkdownColumn()
contacts = tables.ManyToManyColumn(
linkify_item=True
)
tags = TagColumn( tags = TagColumn(
url_name='circuits:provider_list' url_name='circuits:provider_list'
) )
@ -66,7 +69,7 @@ class ProviderTable(BaseTable):
model = Provider model = Provider
fields = ( fields = (
'pk', 'id', 'name', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'circuit_count', 'pk', 'id', 'name', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'circuit_count',
'comments', 'tags', 'created', 'last_updated', 'comments', 'contacts', 'tags', 'created', 'last_updated',
) )
default_columns = ('pk', 'name', 'asn', 'account', 'circuit_count') default_columns = ('pk', 'name', 'asn', 'account', 'circuit_count')
@ -142,6 +145,9 @@ class CircuitTable(BaseTable):
) )
commit_rate = CommitRateColumn() commit_rate = CommitRateColumn()
comments = MarkdownColumn() comments = MarkdownColumn()
contacts = tables.ManyToManyColumn(
linkify_item=True
)
tags = TagColumn( tags = TagColumn(
url_name='circuits:circuit_list' url_name='circuits:circuit_list'
) )
@ -150,7 +156,7 @@ class CircuitTable(BaseTable):
model = Circuit model = Circuit
fields = ( fields = (
'pk', 'id', 'cid', 'provider', 'type', 'status', 'tenant', 'termination_a', 'termination_z', 'install_date', 'pk', 'id', 'cid', 'provider', 'type', 'status', 'tenant', 'termination_a', 'termination_z', 'install_date',
'commit_rate', 'description', 'comments', 'tags', 'created', 'last_updated', 'commit_rate', 'description', 'comments', 'contacts', 'tags', 'created', 'last_updated',
) )
default_columns = ( default_columns = (
'pk', 'cid', 'provider', 'type', 'status', 'tenant', 'termination_a', 'termination_z', 'description', 'pk', 'cid', 'provider', 'type', 'status', 'tenant', 'termination_a', 'termination_z', 'description',

View File

@ -7,8 +7,8 @@ from ipam.models import ASN
from netbox.filtersets import ( from netbox.filtersets import (
BaseFilterSet, ChangeLoggedModelFilterSet, OrganizationalModelFilterSet, PrimaryModelFilterSet, BaseFilterSet, ChangeLoggedModelFilterSet, OrganizationalModelFilterSet, PrimaryModelFilterSet,
) )
from tenancy.filtersets import TenancyFilterSet from tenancy.filtersets import TenancyFilterSet, ContactModelFilterSet
from tenancy.models import Tenant from tenancy.models import *
from utilities.choices import ColorChoices from utilities.choices import ColorChoices
from utilities.filters import ( from utilities.filters import (
ContentTypeFilter, MultiValueCharFilter, MultiValueMACAddressFilter, MultiValueNumberFilter, MultiValueWWNFilter, ContentTypeFilter, MultiValueCharFilter, MultiValueMACAddressFilter, MultiValueNumberFilter, MultiValueWWNFilter,
@ -62,7 +62,7 @@ __all__ = (
) )
class RegionFilterSet(OrganizationalModelFilterSet): class RegionFilterSet(OrganizationalModelFilterSet, ContactModelFilterSet):
parent_id = django_filters.ModelMultipleChoiceFilter( parent_id = django_filters.ModelMultipleChoiceFilter(
queryset=Region.objects.all(), queryset=Region.objects.all(),
label='Parent region (ID)', label='Parent region (ID)',
@ -80,7 +80,7 @@ class RegionFilterSet(OrganizationalModelFilterSet):
fields = ['id', 'name', 'slug', 'description'] fields = ['id', 'name', 'slug', 'description']
class SiteGroupFilterSet(OrganizationalModelFilterSet): class SiteGroupFilterSet(OrganizationalModelFilterSet, ContactModelFilterSet):
parent_id = django_filters.ModelMultipleChoiceFilter( parent_id = django_filters.ModelMultipleChoiceFilter(
queryset=SiteGroup.objects.all(), queryset=SiteGroup.objects.all(),
label='Parent site group (ID)', label='Parent site group (ID)',
@ -98,7 +98,7 @@ class SiteGroupFilterSet(OrganizationalModelFilterSet):
fields = ['id', 'name', 'slug', 'description'] fields = ['id', 'name', 'slug', 'description']
class SiteFilterSet(PrimaryModelFilterSet, TenancyFilterSet): class SiteFilterSet(PrimaryModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
q = django_filters.CharFilter( q = django_filters.CharFilter(
method='search', method='search',
label='Search', label='Search',
@ -167,7 +167,7 @@ class SiteFilterSet(PrimaryModelFilterSet, TenancyFilterSet):
return queryset.filter(qs_filter) return queryset.filter(qs_filter)
class LocationFilterSet(TenancyFilterSet, OrganizationalModelFilterSet): class LocationFilterSet(TenancyFilterSet, ContactModelFilterSet, OrganizationalModelFilterSet):
region_id = TreeNodeMultipleChoiceFilter( region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(), queryset=Region.objects.all(),
field_name='site__region', field_name='site__region',
@ -240,7 +240,7 @@ class RackRoleFilterSet(OrganizationalModelFilterSet):
fields = ['id', 'name', 'slug', 'color', 'description'] fields = ['id', 'name', 'slug', 'color', 'description']
class RackFilterSet(PrimaryModelFilterSet, TenancyFilterSet): class RackFilterSet(PrimaryModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
q = django_filters.CharFilter( q = django_filters.CharFilter(
method='search', method='search',
label='Search', label='Search',
@ -398,7 +398,7 @@ class RackReservationFilterSet(PrimaryModelFilterSet, TenancyFilterSet):
) )
class ManufacturerFilterSet(OrganizationalModelFilterSet): class ManufacturerFilterSet(OrganizationalModelFilterSet, ContactModelFilterSet):
tag = TagFilter() tag = TagFilter()
class Meta: class Meta:
@ -608,7 +608,7 @@ class PlatformFilterSet(OrganizationalModelFilterSet):
fields = ['id', 'name', 'slug', 'napalm_driver', 'description'] fields = ['id', 'name', 'slug', 'napalm_driver', 'description']
class DeviceFilterSet(PrimaryModelFilterSet, TenancyFilterSet, LocalConfigContextFilterSet): class DeviceFilterSet(PrimaryModelFilterSet, TenancyFilterSet, ContactModelFilterSet, LocalConfigContextFilterSet):
q = django_filters.CharFilter( q = django_filters.CharFilter(
method='search', method='search',
label='Search', label='Search',
@ -1289,7 +1289,7 @@ class CableFilterSet(TenancyFilterSet, PrimaryModelFilterSet):
return queryset return queryset
class PowerPanelFilterSet(PrimaryModelFilterSet): class PowerPanelFilterSet(PrimaryModelFilterSet, ContactModelFilterSet):
q = django_filters.CharFilter( q = django_filters.CharFilter(
method='search', method='search',
label='Search', label='Search',

View File

@ -5,9 +5,10 @@ from django.utils.translation import gettext as _
from dcim.choices import * from dcim.choices import *
from dcim.constants import * from dcim.constants import *
from dcim.models import * from dcim.models import *
from tenancy.models import *
from extras.forms import CustomFieldModelFilterForm, LocalConfigContextFilterForm from extras.forms import CustomFieldModelFilterForm, LocalConfigContextFilterForm
from ipam.models import ASN from ipam.models import ASN
from tenancy.forms import TenancyFilterForm from tenancy.forms import ContactModelFilterForm, TenancyFilterForm
from utilities.forms import ( from utilities.forms import (
APISelectMultiple, add_blank_choice, ColorField, DynamicModelMultipleChoiceField, FilterForm, StaticSelect, APISelectMultiple, add_blank_choice, ColorField, DynamicModelMultipleChoiceField, FilterForm, StaticSelect,
StaticSelectMultiple, TagFilterField, BOOLEAN_WITH_BLANK_CHOICES, StaticSelectMultiple, TagFilterField, BOOLEAN_WITH_BLANK_CHOICES,
@ -98,8 +99,13 @@ class DeviceComponentFilterForm(CustomFieldModelFilterForm):
) )
class RegionFilterForm(CustomFieldModelFilterForm): class RegionFilterForm(ContactModelFilterForm, CustomFieldModelFilterForm):
model = Region model = Region
field_groups = [
['q', 'tag'],
['parent_id'],
['contact', 'contact_role'],
]
parent_id = DynamicModelMultipleChoiceField( parent_id = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(), queryset=Region.objects.all(),
required=False, required=False,
@ -108,8 +114,13 @@ class RegionFilterForm(CustomFieldModelFilterForm):
tag = TagFilterField(model) tag = TagFilterField(model)
class SiteGroupFilterForm(CustomFieldModelFilterForm): class SiteGroupFilterForm(ContactModelFilterForm, CustomFieldModelFilterForm):
model = SiteGroup model = SiteGroup
field_groups = [
['q', 'tag'],
['parent_id'],
['contact', 'contact_role'],
]
parent_id = DynamicModelMultipleChoiceField( parent_id = DynamicModelMultipleChoiceField(
queryset=SiteGroup.objects.all(), queryset=SiteGroup.objects.all(),
required=False, required=False,
@ -118,13 +129,14 @@ class SiteGroupFilterForm(CustomFieldModelFilterForm):
tag = TagFilterField(model) tag = TagFilterField(model)
class SiteFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): class SiteFilterForm(TenancyFilterForm, ContactModelFilterForm, CustomFieldModelFilterForm):
model = Site model = Site
field_groups = [ field_groups = [
['q', 'tag'], ['q', 'tag'],
['status', 'region_id', 'group_id'], ['status', 'region_id', 'group_id'],
['tenant_group_id', 'tenant_id'], ['tenant_group_id', 'tenant_id'],
['asn_id'] ['asn_id'],
['contact', 'contact_role'],
] ]
status = forms.MultipleChoiceField( status = forms.MultipleChoiceField(
choices=SiteStatusChoices, choices=SiteStatusChoices,
@ -149,12 +161,13 @@ class SiteFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
tag = TagFilterField(model) tag = TagFilterField(model)
class LocationFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): class LocationFilterForm(TenancyFilterForm, ContactModelFilterForm, CustomFieldModelFilterForm):
model = Location model = Location
field_groups = [ field_groups = [
['q', 'tag'], ['q', 'tag'],
['region_id', 'site_group_id', 'site_id', 'parent_id'], ['region_id', 'site_group_id', 'site_id', 'parent_id'],
['tenant_group_id', 'tenant_id'], ['tenant_group_id', 'tenant_id'],
['contact', 'contact_role'],
] ]
region_id = DynamicModelMultipleChoiceField( region_id = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(), queryset=Region.objects.all(),
@ -192,7 +205,7 @@ class RackRoleFilterForm(CustomFieldModelFilterForm):
tag = TagFilterField(model) tag = TagFilterField(model)
class RackFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): class RackFilterForm(TenancyFilterForm, ContactModelFilterForm, CustomFieldModelFilterForm):
model = Rack model = Rack
field_groups = [ field_groups = [
['q', 'tag'], ['q', 'tag'],
@ -200,6 +213,7 @@ class RackFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
['status', 'role_id'], ['status', 'role_id'],
['type', 'width', 'serial', 'asset_tag'], ['type', 'width', 'serial', 'asset_tag'],
['tenant_group_id', 'tenant_id'], ['tenant_group_id', 'tenant_id'],
['contact', 'contact_role']
] ]
region_id = DynamicModelMultipleChoiceField( region_id = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(), queryset=Region.objects.all(),
@ -303,8 +317,12 @@ class RackReservationFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
tag = TagFilterField(model) tag = TagFilterField(model)
class ManufacturerFilterForm(CustomFieldModelFilterForm): class ManufacturerFilterForm(ContactModelFilterForm, CustomFieldModelFilterForm):
model = Manufacturer model = Manufacturer
field_groups = [
['q', 'tag'],
['contact', 'contact_role'],
]
tag = TagFilterField(model) tag = TagFilterField(model)
@ -390,7 +408,7 @@ class PlatformFilterForm(CustomFieldModelFilterForm):
tag = TagFilterField(model) tag = TagFilterField(model)
class DeviceFilterForm(LocalConfigContextFilterForm, TenancyFilterForm, CustomFieldModelFilterForm): class DeviceFilterForm(LocalConfigContextFilterForm, TenancyFilterForm, ContactModelFilterForm, CustomFieldModelFilterForm):
model = Device model = Device
field_groups = [ field_groups = [
['q', 'tag'], ['q', 'tag'],
@ -402,6 +420,7 @@ class DeviceFilterForm(LocalConfigContextFilterForm, TenancyFilterForm, CustomFi
'has_primary_ip', 'virtual_chassis_member', 'console_ports', 'console_server_ports', 'power_ports', 'has_primary_ip', 'virtual_chassis_member', 'console_ports', 'console_server_ports', 'power_ports',
'power_outlets', 'interfaces', 'pass_through_ports', 'local_context_data', 'power_outlets', 'interfaces', 'pass_through_ports', 'local_context_data',
], ],
['contact', 'contact_role'],
] ]
region_id = DynamicModelMultipleChoiceField( region_id = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(), queryset=Region.objects.all(),
@ -636,11 +655,12 @@ class CableFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
tag = TagFilterField(model) tag = TagFilterField(model)
class PowerPanelFilterForm(CustomFieldModelFilterForm): class PowerPanelFilterForm(ContactModelFilterForm, CustomFieldModelFilterForm):
model = PowerPanel model = PowerPanel
field_groups = ( field_groups = (
('q', 'tag'), ('q', 'tag'),
('region_id', 'site_group_id', 'site_id', 'location_id') ('region_id', 'site_group_id', 'site_id', 'location_id'),
('contact', 'contact_role')
) )
region_id = DynamicModelMultipleChoiceField( region_id = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(), queryset=Region.objects.all(),

View File

@ -194,6 +194,9 @@ class DeviceTable(BaseTable):
vc_priority = tables.Column( vc_priority = tables.Column(
verbose_name='VC Priority' verbose_name='VC Priority'
) )
contacts = tables.ManyToManyColumn(
linkify_item=True
)
comments = MarkdownColumn() comments = MarkdownColumn()
tags = TagColumn( tags = TagColumn(
url_name='dcim:device_list' url_name='dcim:device_list'
@ -204,8 +207,8 @@ class DeviceTable(BaseTable):
fields = ( fields = (
'pk', 'id', 'name', 'status', 'tenant', 'device_role', 'manufacturer', 'device_type', 'platform', 'serial', 'pk', 'id', 'name', 'status', 'tenant', 'device_role', 'manufacturer', 'device_type', 'platform', 'serial',
'asset_tag', 'site', 'location', 'rack', 'position', 'face', 'primary_ip', 'airflow', 'primary_ip4', 'asset_tag', 'site', 'location', 'rack', 'position', 'face', 'primary_ip', 'airflow', 'primary_ip4',
'primary_ip6', 'cluster', 'virtual_chassis', 'vc_position', 'vc_priority', 'comments', 'tags', 'created', 'primary_ip6', 'cluster', 'virtual_chassis', 'vc_position', 'vc_priority', 'comments', 'contacts', 'tags',
'last_updated', 'created', 'last_updated',
) )
default_columns = ( default_columns = (
'pk', 'name', 'status', 'tenant', 'site', 'location', 'rack', 'device_role', 'manufacturer', 'device_type', 'pk', 'name', 'status', 'tenant', 'site', 'location', 'rack', 'device_role', 'manufacturer', 'device_type',

View File

@ -41,6 +41,9 @@ class ManufacturerTable(BaseTable):
verbose_name='Platforms' verbose_name='Platforms'
) )
slug = tables.Column() slug = tables.Column()
contacts = tables.ManyToManyColumn(
linkify_item=True
)
tags = TagColumn( tags = TagColumn(
url_name='dcim:manufacturer_list' url_name='dcim:manufacturer_list'
) )
@ -50,7 +53,7 @@ class ManufacturerTable(BaseTable):
model = Manufacturer model = Manufacturer
fields = ( fields = (
'pk', 'id', 'name', 'devicetype_count', 'inventoryitem_count', 'platform_count', 'description', 'slug', 'pk', 'id', 'name', 'devicetype_count', 'inventoryitem_count', 'platform_count', 'description', 'slug',
'actions', 'created', 'last_updated', 'contacts', 'actions', 'created', 'last_updated',
) )
default_columns = ( default_columns = (
'pk', 'name', 'devicetype_count', 'inventoryitem_count', 'platform_count', 'description', 'slug', 'actions', 'pk', 'name', 'devicetype_count', 'inventoryitem_count', 'platform_count', 'description', 'slug', 'actions',

View File

@ -27,13 +27,16 @@ class PowerPanelTable(BaseTable):
url_params={'power_panel_id': 'pk'}, url_params={'power_panel_id': 'pk'},
verbose_name='Feeds' verbose_name='Feeds'
) )
contacts = tables.ManyToManyColumn(
linkify_item=True
)
tags = TagColumn( tags = TagColumn(
url_name='dcim:powerpanel_list' url_name='dcim:powerpanel_list'
) )
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = PowerPanel model = PowerPanel
fields = ('pk', 'id', 'name', 'site', 'location', 'powerfeed_count', 'tags', 'created', 'last_updated',) fields = ('pk', 'id', 'name', 'site', 'location', 'powerfeed_count', 'contacts', 'tags', 'created', 'last_updated',)
default_columns = ('pk', 'name', 'site', 'location', 'powerfeed_count') default_columns = ('pk', 'name', 'site', 'location', 'powerfeed_count')

View File

@ -75,6 +75,9 @@ class RackTable(BaseTable):
orderable=False, orderable=False,
verbose_name='Power' verbose_name='Power'
) )
contacts = tables.ManyToManyColumn(
linkify_item=True
)
tags = TagColumn( tags = TagColumn(
url_name='dcim:rack_list' url_name='dcim:rack_list'
) )
@ -92,7 +95,7 @@ class RackTable(BaseTable):
fields = ( fields = (
'pk', 'id', 'name', 'site', 'location', 'status', 'facility_id', 'tenant', 'role', 'serial', 'asset_tag', 'pk', 'id', 'name', 'site', 'location', 'status', 'facility_id', 'tenant', 'role', 'serial', 'asset_tag',
'type', 'width', 'outer_width', 'outer_depth', 'u_height', 'comments', 'device_count', 'get_utilization', 'type', 'width', 'outer_width', 'outer_depth', 'u_height', 'comments', 'device_count', 'get_utilization',
'get_power_utilization', 'tags', 'created', 'last_updated', 'get_power_utilization', 'contacts', 'tags', 'created', 'last_updated',
) )
default_columns = ( default_columns = (
'pk', 'name', 'site', 'location', 'status', 'facility_id', 'tenant', 'role', 'u_height', 'device_count', 'pk', 'name', 'site', 'location', 'status', 'facility_id', 'tenant', 'role', 'u_height', 'device_count',

View File

@ -29,6 +29,9 @@ class RegionTable(BaseTable):
url_params={'region_id': 'pk'}, url_params={'region_id': 'pk'},
verbose_name='Sites' verbose_name='Sites'
) )
contacts = tables.ManyToManyColumn(
linkify_item=True
)
tags = TagColumn( tags = TagColumn(
url_name='dcim:region_list' url_name='dcim:region_list'
) )
@ -36,7 +39,7 @@ class RegionTable(BaseTable):
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = Region model = Region
fields = ('pk', 'id', 'name', 'slug', 'site_count', 'description', 'tags', 'actions', 'created', 'last_updated') fields = ('pk', 'id', 'name', 'slug', 'site_count', 'description', 'contacts', 'tags', 'actions', 'created', 'last_updated')
default_columns = ('pk', 'name', 'site_count', 'description', 'actions') default_columns = ('pk', 'name', 'site_count', 'description', 'actions')
@ -54,6 +57,9 @@ class SiteGroupTable(BaseTable):
url_params={'group_id': 'pk'}, url_params={'group_id': 'pk'},
verbose_name='Sites' verbose_name='Sites'
) )
contacts = tables.ManyToManyColumn(
linkify_item=True
)
tags = TagColumn( tags = TagColumn(
url_name='dcim:sitegroup_list' url_name='dcim:sitegroup_list'
) )
@ -61,7 +67,7 @@ class SiteGroupTable(BaseTable):
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = SiteGroup model = SiteGroup
fields = ('pk', 'id', 'name', 'slug', 'site_count', 'description', 'tags', 'actions', 'created', 'last_updated') fields = ('pk', 'id', 'name', 'slug', 'site_count', 'description', 'contacts', 'tags', 'actions', 'created', 'last_updated')
default_columns = ('pk', 'name', 'site_count', 'description', 'actions') default_columns = ('pk', 'name', 'site_count', 'description', 'actions')
@ -92,6 +98,9 @@ class SiteTable(BaseTable):
verbose_name='ASNs' verbose_name='ASNs'
) )
tenant = TenantColumn() tenant = TenantColumn()
contacts = tables.ManyToManyColumn(
linkify_item=True
)
comments = MarkdownColumn() comments = MarkdownColumn()
tags = TagColumn( tags = TagColumn(
url_name='dcim:site_list' url_name='dcim:site_list'
@ -102,7 +111,7 @@ class SiteTable(BaseTable):
fields = ( fields = (
'pk', 'id', 'name', 'slug', 'status', 'facility', 'region', 'group', 'tenant', 'asns', 'asn_count', 'pk', 'id', 'name', 'slug', 'status', 'facility', 'region', 'group', 'tenant', 'asns', 'asn_count',
'time_zone', 'description', 'physical_address', 'shipping_address', 'latitude', 'longitude', 'contact_name', 'time_zone', 'description', 'physical_address', 'shipping_address', 'latitude', 'longitude', 'contact_name',
'contact_phone', 'contact_email', 'comments', 'tags', 'created', 'last_updated', 'contact_phone', 'contact_email', 'contacts', 'comments', 'tags', 'created', 'last_updated',
) )
default_columns = ('pk', 'name', 'status', 'facility', 'region', 'group', 'tenant', 'description') default_columns = ('pk', 'name', 'status', 'facility', 'region', 'group', 'tenant', 'description')
@ -130,6 +139,9 @@ class LocationTable(BaseTable):
url_params={'location_id': 'pk'}, url_params={'location_id': 'pk'},
verbose_name='Devices' verbose_name='Devices'
) )
contacts = tables.ManyToManyColumn(
linkify_item=True
)
tags = TagColumn( tags = TagColumn(
url_name='dcim:location_list' url_name='dcim:location_list'
) )
@ -141,7 +153,7 @@ class LocationTable(BaseTable):
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = Location model = Location
fields = ( fields = (
'pk', 'id', 'name', 'site', 'tenant', 'rack_count', 'device_count', 'description', 'slug', 'tags', 'pk', 'id', 'name', 'site', 'tenant', 'rack_count', 'device_count', 'description', 'slug', 'contacts',
'actions', 'created', 'last_updated', 'tags', 'actions', 'created', 'last_updated',
) )
default_columns = ('pk', 'name', 'site', 'tenant', 'rack_count', 'device_count', 'description', 'actions') default_columns = ('pk', 'name', 'site', 'tenant', 'rack_count', 'device_count', 'description', 'actions')

View File

@ -11,6 +11,7 @@ __all__ = (
'ContactAssignmentFilterSet', 'ContactAssignmentFilterSet',
'ContactFilterSet', 'ContactFilterSet',
'ContactGroupFilterSet', 'ContactGroupFilterSet',
'ContactModelFilterSet',
'ContactRoleFilterSet', 'ContactRoleFilterSet',
'TenancyFilterSet', 'TenancyFilterSet',
'TenantFilterSet', 'TenantFilterSet',
@ -18,92 +19,6 @@ __all__ = (
) )
#
# Tenancy
#
class TenantGroupFilterSet(OrganizationalModelFilterSet):
parent_id = django_filters.ModelMultipleChoiceFilter(
queryset=TenantGroup.objects.all(),
label='Tenant group (ID)',
)
parent = django_filters.ModelMultipleChoiceFilter(
field_name='parent__slug',
queryset=TenantGroup.objects.all(),
to_field_name='slug',
label='Tenant group (slug)',
)
tag = TagFilter()
class Meta:
model = TenantGroup
fields = ['id', 'name', 'slug', 'description']
class TenantFilterSet(PrimaryModelFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
)
group_id = TreeNodeMultipleChoiceFilter(
queryset=TenantGroup.objects.all(),
field_name='group',
lookup_expr='in',
label='Tenant group (ID)',
)
group = TreeNodeMultipleChoiceFilter(
queryset=TenantGroup.objects.all(),
field_name='group',
lookup_expr='in',
to_field_name='slug',
label='Tenant group (slug)',
)
tag = TagFilter()
class Meta:
model = Tenant
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 TenancyFilterSet(django_filters.FilterSet):
"""
An inheritable FilterSet for models which support Tenant assignment.
"""
tenant_group_id = TreeNodeMultipleChoiceFilter(
queryset=TenantGroup.objects.all(),
field_name='tenant__group',
lookup_expr='in',
label='Tenant Group (ID)',
)
tenant_group = TreeNodeMultipleChoiceFilter(
queryset=TenantGroup.objects.all(),
field_name='tenant__group',
to_field_name='slug',
lookup_expr='in',
label='Tenant Group (slug)',
)
tenant_id = django_filters.ModelMultipleChoiceFilter(
queryset=Tenant.objects.all(),
label='Tenant (ID)',
)
tenant = django_filters.ModelMultipleChoiceFilter(
queryset=Tenant.objects.all(),
field_name='tenant__slug',
to_field_name='slug',
label='Tenant (slug)',
)
# #
# Contacts # Contacts
# #
@ -191,3 +106,102 @@ class ContactAssignmentFilterSet(ChangeLoggedModelFilterSet):
class Meta: class Meta:
model = ContactAssignment model = ContactAssignment
fields = ['id', 'content_type_id', 'object_id', 'priority'] fields = ['id', 'content_type_id', 'object_id', 'priority']
class ContactModelFilterSet(django_filters.FilterSet):
contact = django_filters.ModelMultipleChoiceFilter(
field_name='contacts__contact',
queryset=Contact.objects.all(),
label='Contact',
)
contact_role = django_filters.ModelMultipleChoiceFilter(
field_name='contacts__role',
queryset=ContactRole.objects.all(),
label='Contact Role'
)
#
# Tenancy
#
class TenantGroupFilterSet(OrganizationalModelFilterSet):
parent_id = django_filters.ModelMultipleChoiceFilter(
queryset=TenantGroup.objects.all(),
label='Tenant group (ID)',
)
parent = django_filters.ModelMultipleChoiceFilter(
field_name='parent__slug',
queryset=TenantGroup.objects.all(),
to_field_name='slug',
label='Tenant group (slug)',
)
tag = TagFilter()
class Meta:
model = TenantGroup
fields = ['id', 'name', 'slug', 'description']
class TenantFilterSet(PrimaryModelFilterSet, ContactModelFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
)
group_id = TreeNodeMultipleChoiceFilter(
queryset=TenantGroup.objects.all(),
field_name='group',
lookup_expr='in',
label='Tenant group (ID)',
)
group = TreeNodeMultipleChoiceFilter(
queryset=TenantGroup.objects.all(),
field_name='group',
lookup_expr='in',
to_field_name='slug',
label='Tenant group (slug)',
)
tag = TagFilter()
class Meta:
model = Tenant
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 TenancyFilterSet(django_filters.FilterSet):
"""
An inheritable FilterSet for models which support Tenant assignment.
"""
tenant_group_id = TreeNodeMultipleChoiceFilter(
queryset=TenantGroup.objects.all(),
field_name='tenant__group',
lookup_expr='in',
label='Tenant Group (ID)',
)
tenant_group = TreeNodeMultipleChoiceFilter(
queryset=TenantGroup.objects.all(),
field_name='tenant__group',
to_field_name='slug',
lookup_expr='in',
label='Tenant Group (slug)',
)
tenant_id = django_filters.ModelMultipleChoiceFilter(
queryset=Tenant.objects.all(),
label='Tenant (ID)',
)
tenant = django_filters.ModelMultipleChoiceFilter(
queryset=Tenant.objects.all(),
field_name='tenant__slug',
to_field_name='slug',
label='Tenant (slug)',
)

View File

@ -2,6 +2,7 @@ from django.utils.translation import gettext as _
from extras.forms import CustomFieldModelFilterForm from extras.forms import CustomFieldModelFilterForm
from tenancy.models import * from tenancy.models import *
from tenancy.forms import ContactModelFilterForm
from utilities.forms import DynamicModelMultipleChoiceField, TagFilterField from utilities.forms import DynamicModelMultipleChoiceField, TagFilterField
__all__ = ( __all__ = (
@ -27,11 +28,12 @@ class TenantGroupFilterForm(CustomFieldModelFilterForm):
tag = TagFilterField(model) tag = TagFilterField(model)
class TenantFilterForm(CustomFieldModelFilterForm): class TenantFilterForm(ContactModelFilterForm, CustomFieldModelFilterForm):
model = Tenant model = Tenant
field_groups = ( field_groups = (
('q', 'tag'), ('q', 'tag'),
('group_id',), ('group_id',),
('contact', 'contact_role')
) )
group_id = DynamicModelMultipleChoiceField( group_id = DynamicModelMultipleChoiceField(
queryset=TenantGroup.objects.all(), queryset=TenantGroup.objects.all(),

View File

@ -1,10 +1,11 @@
from django import forms from django import forms
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from tenancy.models import Tenant, TenantGroup from tenancy.models import *
from utilities.forms import DynamicModelChoiceField, DynamicModelMultipleChoiceField from utilities.forms import DynamicModelChoiceField, DynamicModelMultipleChoiceField
__all__ = ( __all__ = (
'ContactModelFilterForm',
'TenancyForm', 'TenancyForm',
'TenancyFilterForm', 'TenancyFilterForm',
) )
@ -44,3 +45,16 @@ class TenancyFilterForm(forms.Form):
}, },
label=_('Tenant') label=_('Tenant')
) )
class ContactModelFilterForm(forms.Form):
contact = DynamicModelMultipleChoiceField(
queryset=Contact.objects.all(),
required=False,
label=_('Contact')
)
contact_role = DynamicModelMultipleChoiceField(
queryset=ContactRole.objects.all(),
required=False,
label=_('Contact Role')
)

View File

@ -166,3 +166,6 @@ class ContactAssignment(ChangeLoggedModel):
if self.priority: if self.priority:
return f"{self.contact} ({self.get_priority_display()})" return f"{self.contact} ({self.get_priority_display()})"
return str(self.contact) return str(self.contact)
def get_absolute_url(self):
return reverse('tenancy:contact', args=[self.contact.pk])

View File

@ -77,6 +77,9 @@ class TenantTable(BaseTable):
group = tables.Column( group = tables.Column(
linkify=True linkify=True
) )
contacts = tables.ManyToManyColumn(
linkify_item=True
)
comments = MarkdownColumn() comments = MarkdownColumn()
tags = TagColumn( tags = TagColumn(
url_name='tenancy:tenant_list' url_name='tenancy:tenant_list'
@ -84,7 +87,7 @@ class TenantTable(BaseTable):
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = Tenant model = Tenant
fields = ('pk', 'id', 'name', 'slug', 'group', 'description', 'comments', 'tags', 'created', 'last_updated',) fields = ('pk', 'id', 'name', 'slug', 'group', 'description', 'comments', 'contacts', 'tags', 'created', 'last_updated',)
default_columns = ('pk', 'name', 'group', 'description') default_columns = ('pk', 'name', 'group', 'description')

View File

@ -5,7 +5,7 @@ from dcim.models import DeviceRole, Platform, Region, Site, SiteGroup
from extras.filters import TagFilter from extras.filters import TagFilter
from extras.filtersets import LocalConfigContextFilterSet from extras.filtersets import LocalConfigContextFilterSet
from netbox.filtersets import OrganizationalModelFilterSet, PrimaryModelFilterSet from netbox.filtersets import OrganizationalModelFilterSet, PrimaryModelFilterSet
from tenancy.filtersets import TenancyFilterSet from tenancy.filtersets import TenancyFilterSet, ContactModelFilterSet
from utilities.filters import MultiValueMACAddressFilter, TreeNodeMultipleChoiceFilter from utilities.filters import MultiValueMACAddressFilter, TreeNodeMultipleChoiceFilter
from .choices import * from .choices import *
from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine, VMInterface from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine, VMInterface
@ -27,7 +27,7 @@ class ClusterTypeFilterSet(OrganizationalModelFilterSet):
fields = ['id', 'name', 'slug', 'description'] fields = ['id', 'name', 'slug', 'description']
class ClusterGroupFilterSet(OrganizationalModelFilterSet): class ClusterGroupFilterSet(OrganizationalModelFilterSet, ContactModelFilterSet):
tag = TagFilter() tag = TagFilter()
class Meta: class Meta:
@ -35,7 +35,7 @@ class ClusterGroupFilterSet(OrganizationalModelFilterSet):
fields = ['id', 'name', 'slug', 'description'] fields = ['id', 'name', 'slug', 'description']
class ClusterFilterSet(PrimaryModelFilterSet, TenancyFilterSet): class ClusterFilterSet(PrimaryModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
q = django_filters.CharFilter( q = django_filters.CharFilter(
method='search', method='search',
label='Search', label='Search',
@ -111,7 +111,7 @@ class ClusterFilterSet(PrimaryModelFilterSet, TenancyFilterSet):
) )
class VirtualMachineFilterSet(PrimaryModelFilterSet, TenancyFilterSet, LocalConfigContextFilterSet): class VirtualMachineFilterSet(PrimaryModelFilterSet, TenancyFilterSet, ContactModelFilterSet, LocalConfigContextFilterSet):
q = django_filters.CharFilter( q = django_filters.CharFilter(
method='search', method='search',
label='Search', label='Search',

View File

@ -3,7 +3,7 @@ from django.utils.translation import gettext as _
from dcim.models import DeviceRole, Platform, Region, Site, SiteGroup from dcim.models import DeviceRole, Platform, Region, Site, SiteGroup
from extras.forms import CustomFieldModelFilterForm, LocalConfigContextFilterForm from extras.forms import CustomFieldModelFilterForm, LocalConfigContextFilterForm
from tenancy.forms import TenancyFilterForm from tenancy.forms import TenancyFilterForm, ContactModelFilterForm
from utilities.forms import ( from utilities.forms import (
DynamicModelMultipleChoiceField, StaticSelect, StaticSelectMultiple, TagFilterField, BOOLEAN_WITH_BLANK_CHOICES, DynamicModelMultipleChoiceField, StaticSelect, StaticSelectMultiple, TagFilterField, BOOLEAN_WITH_BLANK_CHOICES,
) )
@ -24,18 +24,19 @@ class ClusterTypeFilterForm(CustomFieldModelFilterForm):
tag = TagFilterField(model) tag = TagFilterField(model)
class ClusterGroupFilterForm(CustomFieldModelFilterForm): class ClusterGroupFilterForm(ContactModelFilterForm, CustomFieldModelFilterForm):
model = ClusterGroup model = ClusterGroup
tag = TagFilterField(model) tag = TagFilterField(model)
class ClusterFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): class ClusterFilterForm(TenancyFilterForm, ContactModelFilterForm, CustomFieldModelFilterForm):
model = Cluster model = Cluster
field_groups = [ field_groups = [
['q', 'tag'], ['q', 'tag'],
['group_id', 'type_id'], ['group_id', 'type_id'],
['region_id', 'site_group_id', 'site_id'], ['region_id', 'site_group_id', 'site_id'],
['tenant_group_id', 'tenant_id'], ['tenant_group_id', 'tenant_id'],
['contact', 'contact_role'],
] ]
type_id = DynamicModelMultipleChoiceField( type_id = DynamicModelMultipleChoiceField(
queryset=ClusterType.objects.all(), queryset=ClusterType.objects.all(),
@ -71,7 +72,7 @@ class ClusterFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
tag = TagFilterField(model) tag = TagFilterField(model)
class VirtualMachineFilterForm(LocalConfigContextFilterForm, TenancyFilterForm, CustomFieldModelFilterForm): class VirtualMachineFilterForm(LocalConfigContextFilterForm, TenancyFilterForm, ContactModelFilterForm, CustomFieldModelFilterForm):
model = VirtualMachine model = VirtualMachine
field_groups = [ field_groups = [
['q', 'tag'], ['q', 'tag'],
@ -79,6 +80,7 @@ class VirtualMachineFilterForm(LocalConfigContextFilterForm, TenancyFilterForm,
['region_id', 'site_group_id', 'site_id'], ['region_id', 'site_group_id', 'site_id'],
['status', 'role_id', 'platform_id', 'mac_address', 'has_primary_ip', 'local_context_data'], ['status', 'role_id', 'platform_id', 'mac_address', 'has_primary_ip', 'local_context_data'],
['tenant_group_id', 'tenant_id'], ['tenant_group_id', 'tenant_id'],
['contact', 'contact_role'],
] ]
cluster_group_id = DynamicModelMultipleChoiceField( cluster_group_id = DynamicModelMultipleChoiceField(
queryset=ClusterGroup.objects.all(), queryset=ClusterGroup.objects.all(),

View File

@ -62,6 +62,9 @@ class ClusterGroupTable(BaseTable):
cluster_count = tables.Column( cluster_count = tables.Column(
verbose_name='Clusters' verbose_name='Clusters'
) )
contacts = tables.ManyToManyColumn(
linkify_item=True
)
tags = TagColumn( tags = TagColumn(
url_name='virtualization:clustergroup_list' url_name='virtualization:clustergroup_list'
) )
@ -70,7 +73,7 @@ class ClusterGroupTable(BaseTable):
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = ClusterGroup model = ClusterGroup
fields = ( fields = (
'pk', 'id', 'name', 'slug', 'cluster_count', 'description', 'tags', 'actions', 'created', 'last_updated', 'pk', 'id', 'name', 'slug', 'cluster_count', 'description', 'contacts', 'tags', 'actions', 'created', 'last_updated',
) )
default_columns = ('pk', 'name', 'cluster_count', 'description', 'actions') default_columns = ('pk', 'name', 'cluster_count', 'description', 'actions')
@ -106,6 +109,9 @@ class ClusterTable(BaseTable):
url_params={'cluster_id': 'pk'}, url_params={'cluster_id': 'pk'},
verbose_name='VMs' verbose_name='VMs'
) )
contacts = tables.ManyToManyColumn(
linkify_item=True
)
comments = MarkdownColumn() comments = MarkdownColumn()
tags = TagColumn( tags = TagColumn(
url_name='virtualization:cluster_list' url_name='virtualization:cluster_list'
@ -114,7 +120,7 @@ class ClusterTable(BaseTable):
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = Cluster model = Cluster
fields = ( fields = (
'pk', 'id', 'name', 'type', 'group', 'tenant', 'site', 'comments', 'device_count', 'vm_count', 'tags', 'pk', 'id', 'name', 'type', 'group', 'tenant', 'site', 'comments', 'device_count', 'vm_count', 'contacts', 'tags',
'created', 'last_updated', 'created', 'last_updated',
) )
default_columns = ('pk', 'name', 'type', 'group', 'tenant', 'site', 'device_count', 'vm_count') default_columns = ('pk', 'name', 'type', 'group', 'tenant', 'site', 'device_count', 'vm_count')
@ -150,6 +156,9 @@ class VirtualMachineTable(BaseTable):
order_by=('primary_ip4', 'primary_ip6'), order_by=('primary_ip4', 'primary_ip6'),
verbose_name='IP Address' verbose_name='IP Address'
) )
contacts = tables.ManyToManyColumn(
linkify_item=True
)
tags = TagColumn( tags = TagColumn(
url_name='virtualization:virtualmachine_list' url_name='virtualization:virtualmachine_list'
) )
@ -158,7 +167,7 @@ class VirtualMachineTable(BaseTable):
model = VirtualMachine model = VirtualMachine
fields = ( fields = (
'pk', 'id', 'name', 'status', 'cluster', 'role', 'tenant', 'platform', 'vcpus', 'memory', 'disk', 'pk', 'id', 'name', 'status', 'cluster', 'role', 'tenant', 'platform', 'vcpus', 'memory', 'disk',
'primary_ip4', 'primary_ip6', 'primary_ip', 'comments', 'tags', 'created', 'last_updated', 'primary_ip4', 'primary_ip6', 'primary_ip', 'comments', 'contacts', 'tags', 'created', 'last_updated',
) )
default_columns = ( default_columns = (
'pk', 'name', 'status', 'cluster', 'role', 'tenant', 'vcpus', 'memory', 'disk', 'primary_ip', 'pk', 'name', 'status', 'cluster', 'role', 'tenant', 'vcpus', 'memory', 'disk', 'primary_ip',