mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Closes #3064: Include tags in object lists as a toggleable table column
This commit is contained in:
@ -6,6 +6,7 @@
|
||||
|
||||
* [#492](https://github.com/netbox-community/netbox/issues/492) - Enable toggling and rearranging table columns
|
||||
* [#3147](https://github.com/netbox-community/netbox/issues/3147) - Allow specifying related objects by arbitrary attribute during CSV import
|
||||
* [#3064](https://github.com/netbox-community/netbox/issues/3064) - Include tags in object lists as a toggleable table column
|
||||
* [#3294](https://github.com/netbox-community/netbox/issues/3294) - Implement mechanism for storing user preferences
|
||||
* [#4421](https://github.com/netbox-community/netbox/issues/4421) - Retain user's preference for config context format
|
||||
* [#4502](https://github.com/netbox-community/netbox/issues/4502) - Enable configuration of proxies for outbound HTTP requests
|
||||
|
@ -2,7 +2,7 @@ import django_tables2 as tables
|
||||
from django_tables2.utils import Accessor
|
||||
|
||||
from tenancy.tables import COL_TENANT
|
||||
from utilities.tables import BaseTable, ToggleColumn
|
||||
from utilities.tables import BaseTable, TagColumn, ToggleColumn
|
||||
from .models import Circuit, CircuitType, Provider
|
||||
|
||||
CIRCUITTYPE_ACTIONS = """
|
||||
@ -31,10 +31,15 @@ class ProviderTable(BaseTable):
|
||||
accessor=Accessor('count_circuits'),
|
||||
verbose_name='Circuits'
|
||||
)
|
||||
tags = TagColumn(
|
||||
url_name='circuits:provider_list'
|
||||
)
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = Provider
|
||||
fields = ('pk', 'name', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'circuit_count')
|
||||
fields = (
|
||||
'pk', 'name', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'circuit_count', 'tags',
|
||||
)
|
||||
default_columns = ('pk', 'name', 'asn', 'account', 'circuit_count')
|
||||
|
||||
|
||||
@ -45,7 +50,9 @@ class ProviderTable(BaseTable):
|
||||
class CircuitTypeTable(BaseTable):
|
||||
pk = ToggleColumn()
|
||||
name = tables.LinkColumn()
|
||||
circuit_count = tables.Column(verbose_name='Circuits')
|
||||
circuit_count = tables.Column(
|
||||
verbose_name='Circuits'
|
||||
)
|
||||
actions = tables.TemplateColumn(
|
||||
template_code=CIRCUITTYPE_ACTIONS,
|
||||
attrs={'td': {'class': 'text-right noprint'}},
|
||||
@ -64,21 +71,33 @@ class CircuitTypeTable(BaseTable):
|
||||
|
||||
class CircuitTable(BaseTable):
|
||||
pk = ToggleColumn()
|
||||
cid = tables.LinkColumn(verbose_name='ID')
|
||||
provider = tables.LinkColumn('circuits:provider', args=[Accessor('provider.slug')])
|
||||
status = tables.TemplateColumn(template_code=STATUS_LABEL, verbose_name='Status')
|
||||
tenant = tables.TemplateColumn(template_code=COL_TENANT)
|
||||
cid = tables.LinkColumn(
|
||||
verbose_name='ID'
|
||||
)
|
||||
provider = tables.LinkColumn(
|
||||
viewname='circuits:provider',
|
||||
args=[Accessor('provider.slug')]
|
||||
)
|
||||
status = tables.TemplateColumn(
|
||||
template_code=STATUS_LABEL
|
||||
)
|
||||
tenant = tables.TemplateColumn(
|
||||
template_code=COL_TENANT
|
||||
)
|
||||
a_side = tables.Column(
|
||||
verbose_name='A Side'
|
||||
)
|
||||
z_side = tables.Column(
|
||||
verbose_name='Z Side'
|
||||
)
|
||||
tags = TagColumn(
|
||||
url_name='circuits:circuit_list'
|
||||
)
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = Circuit
|
||||
fields = (
|
||||
'pk', 'cid', 'provider', 'type', 'status', 'tenant', 'a_side', 'z_side', 'install_date', 'commit_rate',
|
||||
'description',
|
||||
'description', 'tags',
|
||||
)
|
||||
default_columns = ('pk', 'cid', 'provider', 'type', 'status', 'tenant', 'a_side', 'z_side', 'description')
|
||||
|
@ -2,7 +2,7 @@ import django_tables2 as tables
|
||||
from django_tables2.utils import Accessor
|
||||
|
||||
from tenancy.tables import COL_TENANT
|
||||
from utilities.tables import BaseTable, BooleanColumn, ColorColumn, ToggleColumn
|
||||
from utilities.tables import BaseTable, BooleanColumn, ColorColumn, TagColumn, ToggleColumn
|
||||
from .models import (
|
||||
Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
|
||||
DeviceBayTemplate, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate,
|
||||
@ -242,13 +242,16 @@ class SiteTable(BaseTable):
|
||||
tenant = tables.TemplateColumn(
|
||||
template_code=COL_TENANT
|
||||
)
|
||||
tags = TagColumn(
|
||||
url_name='dcim:site_list'
|
||||
)
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = Site
|
||||
fields = (
|
||||
'pk', 'name', 'slug', 'status', 'facility', 'region', 'tenant', 'asn', 'time_zone', 'description',
|
||||
'physical_address', 'shipping_address', 'latitude', 'longitude', 'contact_name', 'contact_phone',
|
||||
'contact_email',
|
||||
'contact_email', 'tags',
|
||||
)
|
||||
default_columns = ('pk', 'name', 'status', 'facility', 'region', 'tenant', 'asn', 'description')
|
||||
|
||||
@ -354,11 +357,14 @@ class RackDetailTable(RackTable):
|
||||
orderable=False,
|
||||
verbose_name='Power'
|
||||
)
|
||||
tags = TagColumn(
|
||||
url_name='dcim:rack_list'
|
||||
)
|
||||
|
||||
class Meta(RackTable.Meta):
|
||||
fields = (
|
||||
'pk', 'name', 'site', 'group', 'status', 'facility_id', 'tenant', 'role', 'serial', 'asset_tag', 'type',
|
||||
'width', 'u_height', 'device_count', 'get_utilization', 'get_power_utilization',
|
||||
'width', 'u_height', 'device_count', 'get_utilization', 'get_power_utilization', 'tags',
|
||||
)
|
||||
default_columns = (
|
||||
'pk', 'name', 'site', 'group', 'status', 'facility_id', 'tenant', 'role', 'u_height', 'device_count',
|
||||
@ -450,17 +456,22 @@ class DeviceTypeTable(BaseTable):
|
||||
args=[Accessor('pk')],
|
||||
verbose_name='Device Type'
|
||||
)
|
||||
is_full_depth = BooleanColumn(verbose_name='Full Depth')
|
||||
is_full_depth = BooleanColumn(
|
||||
verbose_name='Full Depth'
|
||||
)
|
||||
instance_count = tables.TemplateColumn(
|
||||
template_code=DEVICETYPE_INSTANCES_TEMPLATE,
|
||||
verbose_name='Instances'
|
||||
)
|
||||
tags = TagColumn(
|
||||
url_name='dcim:devicetype_list'
|
||||
)
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = DeviceType
|
||||
fields = (
|
||||
'pk', 'model', 'manufacturer', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role',
|
||||
'instance_count',
|
||||
'instance_count', 'tags',
|
||||
)
|
||||
default_columns = (
|
||||
'pk', 'model', 'manufacturer', 'part_number', 'u_height', 'is_full_depth', 'instance_count',
|
||||
@ -834,13 +845,16 @@ class DeviceTable(BaseTable):
|
||||
vc_priority = tables.Column(
|
||||
verbose_name='VC Priority'
|
||||
)
|
||||
tags = TagColumn(
|
||||
url_name='dcim:device_list'
|
||||
)
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = Device
|
||||
fields = (
|
||||
'pk', 'name', 'status', 'tenant', 'device_role', 'device_type', 'platform', 'serial', 'asset_tag', 'site',
|
||||
'rack', 'position', 'face', 'primary_ip', 'primary_ip4', 'primary_ip6', 'cluster', 'virtual_chassis',
|
||||
'vc_position', 'vc_priority',
|
||||
'vc_position', 'vc_priority', 'tags',
|
||||
)
|
||||
default_columns = (
|
||||
'pk', 'name', 'status', 'tenant', 'site', 'rack', 'device_role', 'device_type', 'primary_ip',
|
||||
@ -1206,10 +1220,13 @@ class VirtualChassisTable(BaseTable):
|
||||
member_count = tables.Column(
|
||||
verbose_name='Members'
|
||||
)
|
||||
tags = TagColumn(
|
||||
url_name='dcim:virtualchassis_list'
|
||||
)
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = VirtualChassis
|
||||
fields = ('pk', 'name', 'domain', 'member_count')
|
||||
fields = ('pk', 'name', 'domain', 'member_count', 'tags')
|
||||
default_columns = ('pk', 'name', 'domain', 'member_count')
|
||||
|
||||
|
||||
@ -1262,12 +1279,15 @@ class PowerFeedTable(BaseTable):
|
||||
available_power = tables.Column(
|
||||
verbose_name='Available power (VA)'
|
||||
)
|
||||
tags = TagColumn(
|
||||
url_name='dcim:powerfeed_list'
|
||||
)
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = PowerFeed
|
||||
fields = (
|
||||
'pk', 'name', 'power_panel', 'rack', 'status', 'type', 'supply', 'voltage', 'amperage', 'phase',
|
||||
'max_utilization', 'available_power',
|
||||
'max_utilization', 'available_power', 'tags',
|
||||
)
|
||||
default_columns = (
|
||||
'pk', 'name', 'power_panel', 'rack', 'status', 'type', 'supply', 'voltage', 'amperage', 'phase',
|
||||
|
@ -3,7 +3,7 @@ from django_tables2.utils import Accessor
|
||||
|
||||
from dcim.models import Interface
|
||||
from tenancy.tables import COL_TENANT
|
||||
from utilities.tables import BaseTable, BooleanColumn, ToggleColumn
|
||||
from utilities.tables import BaseTable, BooleanColumn, TagColumn, ToggleColumn
|
||||
from .models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
|
||||
|
||||
RIR_UTILIZATION = """
|
||||
@ -199,10 +199,13 @@ class VRFTable(BaseTable):
|
||||
enforce_unique = BooleanColumn(
|
||||
verbose_name='Unique'
|
||||
)
|
||||
tags = TagColumn(
|
||||
url_name='ipam:vrf_list'
|
||||
)
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = VRF
|
||||
fields = ('pk', 'name', 'rd', 'tenant', 'enforce_unique', 'description')
|
||||
fields = ('pk', 'name', 'rd', 'tenant', 'enforce_unique', 'description', 'tags')
|
||||
default_columns = ('pk', 'name', 'rd', 'tenant', 'description')
|
||||
|
||||
|
||||
@ -300,9 +303,13 @@ class AggregateDetailTable(AggregateTable):
|
||||
template_code=UTILIZATION_GRAPH,
|
||||
orderable=False
|
||||
)
|
||||
tags = TagColumn(
|
||||
url_name='ipam:aggregate_list'
|
||||
)
|
||||
|
||||
class Meta(AggregateTable.Meta):
|
||||
fields = ('pk', 'prefix', 'rir', 'child_count', 'utilization', 'date_added', 'description')
|
||||
fields = ('pk', 'prefix', 'rir', 'child_count', 'utilization', 'date_added', 'description', 'tags')
|
||||
default_columns = ('pk', 'prefix', 'rir', 'child_count', 'utilization', 'date_added', 'description')
|
||||
|
||||
|
||||
#
|
||||
@ -388,10 +395,14 @@ class PrefixDetailTable(PrefixTable):
|
||||
tenant = tables.TemplateColumn(
|
||||
template_code=COL_TENANT
|
||||
)
|
||||
tags = TagColumn(
|
||||
url_name='ipam:prefix_list'
|
||||
)
|
||||
|
||||
class Meta(PrefixTable.Meta):
|
||||
fields = (
|
||||
'pk', 'prefix', 'status', 'vrf', 'utilization', 'tenant', 'site', 'vlan', 'role', 'is_pool', 'description',
|
||||
'tags',
|
||||
)
|
||||
default_columns = (
|
||||
'pk', 'prefix', 'status', 'vrf', 'utilization', 'tenant', 'site', 'vlan', 'role', 'description',
|
||||
@ -446,11 +457,14 @@ class IPAddressDetailTable(IPAddressTable):
|
||||
tenant = tables.TemplateColumn(
|
||||
template_code=COL_TENANT
|
||||
)
|
||||
tags = TagColumn(
|
||||
url_name='ipam:ipaddress_list'
|
||||
)
|
||||
|
||||
class Meta(IPAddressTable.Meta):
|
||||
fields = (
|
||||
'pk', 'address', 'vrf', 'status', 'role', 'tenant', 'nat_inside', 'parent', 'interface', 'dns_name',
|
||||
'description',
|
||||
'description', 'tags',
|
||||
)
|
||||
default_columns = (
|
||||
'pk', 'address', 'vrf', 'status', 'role', 'tenant', 'parent', 'interface', 'dns_name', 'description',
|
||||
@ -573,9 +587,13 @@ class VLANDetailTable(VLANTable):
|
||||
tenant = tables.TemplateColumn(
|
||||
template_code=COL_TENANT
|
||||
)
|
||||
tags = TagColumn(
|
||||
url_name='ipam:vlan_list'
|
||||
)
|
||||
|
||||
class Meta(VLANTable.Meta):
|
||||
fields = ('pk', 'vid', 'site', 'group', 'name', 'prefixes', 'tenant', 'status', 'role', 'description')
|
||||
fields = ('pk', 'vid', 'site', 'group', 'name', 'prefixes', 'tenant', 'status', 'role', 'description', 'tags')
|
||||
default_columns = ('pk', 'vid', 'site', 'group', 'name', 'prefixes', 'tenant', 'status', 'role', 'description')
|
||||
|
||||
|
||||
class VLANMemberTable(BaseTable):
|
||||
@ -647,8 +665,11 @@ class ServiceTable(BaseTable):
|
||||
viewname='ipam:service',
|
||||
args=[Accessor('pk')]
|
||||
)
|
||||
tags = TagColumn(
|
||||
url_name='ipam:service_list'
|
||||
)
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = Service
|
||||
fields = ('pk', 'name', 'parent', 'protocol', 'port', 'ipaddresses', 'description')
|
||||
fields = ('pk', 'name', 'parent', 'protocol', 'port', 'ipaddresses', 'description', 'tags')
|
||||
default_columns = ('pk', 'name', 'parent', 'protocol', 'port', 'description')
|
||||
|
@ -1,6 +1,6 @@
|
||||
import django_tables2 as tables
|
||||
|
||||
from utilities.tables import BaseTable, ToggleColumn
|
||||
from utilities.tables import BaseTable, TagColumn, ToggleColumn
|
||||
from .models import SecretRole, Secret
|
||||
|
||||
SECRETROLE_ACTIONS = """
|
||||
@ -42,8 +42,11 @@ class SecretRoleTable(BaseTable):
|
||||
class SecretTable(BaseTable):
|
||||
pk = ToggleColumn()
|
||||
device = tables.LinkColumn()
|
||||
tags = TagColumn(
|
||||
url_name='secrets:secret_list'
|
||||
)
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = Secret
|
||||
fields = ('pk', 'device', 'role', 'name', 'last_updated', 'hash')
|
||||
fields = ('pk', 'device', 'role', 'name', 'last_updated', 'hash', 'tags')
|
||||
default_columns = ('pk', 'device', 'role', 'name', 'last_updated')
|
||||
|
@ -1,5 +1,3 @@
|
||||
{% load helpers %}
|
||||
|
||||
{% if url_name %}<a href="{% url url_name %}?tag={{ tag.slug }}">{% endif %}
|
||||
<span class="label label-default" style="color: {{ tag.color|fgcolor }}; background-color: #{{ tag.color }}">{{ tag }}</span>
|
||||
{% if url_name %}</a>{% endif %}
|
||||
{% if url_name %}<a href="{% url url_name %}?tag={{ tag.slug }}">{% endif %}<span class="label label-default" style="color: {{ tag.color|fgcolor }}; background-color: #{{ tag.color }}">{{ tag }}</span>{% if url_name %}</a>{% endif %}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import django_tables2 as tables
|
||||
|
||||
from utilities.tables import BaseTable, ToggleColumn
|
||||
from utilities.tables import BaseTable, TagColumn, ToggleColumn
|
||||
from .models import Tenant, TenantGroup
|
||||
|
||||
MPTT_LINK = """
|
||||
@ -63,8 +63,11 @@ class TenantGroupTable(BaseTable):
|
||||
class TenantTable(BaseTable):
|
||||
pk = ToggleColumn()
|
||||
name = tables.LinkColumn()
|
||||
tags = TagColumn(
|
||||
url_name='tenancy:tenant_list'
|
||||
)
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = Tenant
|
||||
fields = ('pk', 'name', 'slug', 'group', 'description')
|
||||
fields = ('pk', 'name', 'slug', 'group', 'description', 'tags')
|
||||
default_columns = ('pk', 'name', 'group', 'description')
|
||||
|
@ -1,8 +1,9 @@
|
||||
import django_tables2 as tables
|
||||
from django.core.exceptions import FieldDoesNotExist
|
||||
from django.db.models import ForeignKey
|
||||
from django_tables2.data import TableQuerysetData
|
||||
from django.db.models.fields.related import RelatedField
|
||||
from django.utils.safestring import mark_safe
|
||||
from django_tables2.data import TableQuerysetData
|
||||
|
||||
|
||||
class BaseTable(tables.Table):
|
||||
@ -57,7 +58,7 @@ class BaseTable(tables.Table):
|
||||
field_path = column.accessor.split('.')
|
||||
try:
|
||||
model_field = model._meta.get_field(field_path[0])
|
||||
if isinstance(model_field, ForeignKey):
|
||||
if isinstance(model_field, RelatedField):
|
||||
prefetch_fields.append('__'.join(field_path))
|
||||
except FieldDoesNotExist:
|
||||
pass
|
||||
@ -121,3 +122,22 @@ class ColorColumn(tables.Column):
|
||||
return mark_safe(
|
||||
'<span class="label color-block" style="background-color: #{}"> </span>'.format(value)
|
||||
)
|
||||
|
||||
|
||||
class TagColumn(tables.TemplateColumn):
|
||||
"""
|
||||
Display a list of tags assigned to the object.
|
||||
"""
|
||||
template_code = """
|
||||
{% for tag in value.all %}
|
||||
{% include 'utilities/templatetags/tag.html' %}
|
||||
{% empty %}
|
||||
<span class="text-muted">—</span>
|
||||
{% endfor %}
|
||||
"""
|
||||
|
||||
def __init__(self, url_name=None):
|
||||
super().__init__(
|
||||
template_code=self.template_code,
|
||||
extra_context={'url_name': url_name}
|
||||
)
|
||||
|
@ -3,7 +3,7 @@ from django_tables2.utils import Accessor
|
||||
|
||||
from dcim.models import Interface
|
||||
from tenancy.tables import COL_TENANT
|
||||
from utilities.tables import BaseTable, ToggleColumn
|
||||
from utilities.tables import BaseTable, TagColumn, ToggleColumn
|
||||
from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine
|
||||
|
||||
CLUSTERTYPE_ACTIONS = """
|
||||
@ -108,10 +108,14 @@ class ClusterTable(BaseTable):
|
||||
orderable=False,
|
||||
verbose_name='VMs'
|
||||
)
|
||||
tags = TagColumn(
|
||||
url_name='virtualization:cluster_list'
|
||||
)
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = Cluster
|
||||
fields = ('pk', 'name', 'type', 'group', 'tenant', 'site', 'device_count', 'vm_count')
|
||||
fields = ('pk', 'name', 'type', 'group', 'tenant', 'site', 'device_count', 'vm_count', 'tags')
|
||||
default_columns = ('pk', 'name', 'type', 'group', 'tenant', 'site', 'device_count', 'vm_count')
|
||||
|
||||
|
||||
#
|
||||
@ -156,12 +160,15 @@ class VirtualMachineDetailTable(VirtualMachineTable):
|
||||
verbose_name='IP Address',
|
||||
template_code=VIRTUALMACHINE_PRIMARY_IP
|
||||
)
|
||||
tags = TagColumn(
|
||||
url_name='virtualization:virtualmachine_list'
|
||||
)
|
||||
|
||||
class Meta(BaseTable.Meta):
|
||||
model = VirtualMachine
|
||||
fields = (
|
||||
'pk', 'name', 'status', 'cluster', 'role', 'tenant', 'platform', 'vcpus', 'memory', 'disk', 'primary_ip4',
|
||||
'primary_ip6', 'primary_ip',
|
||||
'primary_ip6', 'primary_ip', 'tags',
|
||||
)
|
||||
default_columns = (
|
||||
'pk', 'name', 'status', 'cluster', 'role', 'tenant', 'vcpus', 'memory', 'disk', 'primary_ip',
|
||||
|
Reference in New Issue
Block a user