From 39a9ebaeee982dc787de5df3b42f66ac9fbe39d4 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Fri, 6 May 2022 10:26:02 -0400 Subject: [PATCH] Fixes #9313: Remove HTML code from CSV output of many-to-many relationships --- docs/release-notes/version-3.2.md | 1 + netbox/circuits/tables/circuits.py | 2 +- netbox/circuits/tables/providers.py | 4 +- netbox/dcim/tables/devices.py | 2 +- netbox/dcim/tables/devicetypes.py | 2 +- netbox/dcim/tables/power.py | 2 +- netbox/dcim/tables/racks.py | 2 +- netbox/dcim/tables/sites.py | 10 +-- netbox/ipam/tables/ip.py | 2 +- netbox/netbox/tables/columns.py | 62 ++++++++++++------- netbox/tenancy/tables/tenants.py | 2 +- netbox/virtualization/tables/clusters.py | 4 +- .../virtualization/tables/virtualmachines.py | 2 +- 13 files changed, 57 insertions(+), 40 deletions(-) diff --git a/docs/release-notes/version-3.2.md b/docs/release-notes/version-3.2.md index 665bfb99e..7b9a9e4b2 100644 --- a/docs/release-notes/version-3.2.md +++ b/docs/release-notes/version-3.2.md @@ -16,6 +16,7 @@ * [#9267](https://github.com/netbox-community/netbox/issues/9267) - Remove invalid entry in IP address role choices * [#9306](https://github.com/netbox-community/netbox/issues/9306) - Include VC master interfaces when selecting a LAG/bridge for a VC member interface * [#9311](https://github.com/netbox-community/netbox/issues/9311) - Permit creating contact assignment without a priority via the REST API +* [#9313](https://github.com/netbox-community/netbox/issues/9313) - Remove HTML code from CSV output of many-to-many relationships --- diff --git a/netbox/circuits/tables/circuits.py b/netbox/circuits/tables/circuits.py index cb8c940b0..40f8918ae 100644 --- a/netbox/circuits/tables/circuits.py +++ b/netbox/circuits/tables/circuits.py @@ -59,7 +59,7 @@ class CircuitTable(NetBoxTable): ) commit_rate = CommitRateColumn() comments = columns.MarkdownColumn() - contacts = tables.ManyToManyColumn( + contacts = columns.ManyToManyColumn( linkify_item=True ) tags = columns.TagColumn( diff --git a/netbox/circuits/tables/providers.py b/netbox/circuits/tables/providers.py index e97ade7d8..0ec6d439d 100644 --- a/netbox/circuits/tables/providers.py +++ b/netbox/circuits/tables/providers.py @@ -14,7 +14,7 @@ class ProviderTable(NetBoxTable): name = tables.Column( linkify=True ) - asns = tables.ManyToManyColumn( + asns = columns.ManyToManyColumn( linkify_item=True, verbose_name='ASNs' ) @@ -31,7 +31,7 @@ class ProviderTable(NetBoxTable): verbose_name='Circuits' ) comments = columns.MarkdownColumn() - contacts = tables.ManyToManyColumn( + contacts = columns.ManyToManyColumn( linkify_item=True ) tags = columns.TagColumn( diff --git a/netbox/dcim/tables/devices.py b/netbox/dcim/tables/devices.py index 25ad1415d..0f015b7f3 100644 --- a/netbox/dcim/tables/devices.py +++ b/netbox/dcim/tables/devices.py @@ -190,7 +190,7 @@ class DeviceTable(NetBoxTable): verbose_name='VC Priority' ) comments = columns.MarkdownColumn() - contacts = tables.ManyToManyColumn( + contacts = columns.ManyToManyColumn( linkify_item=True ) tags = columns.TagColumn( diff --git a/netbox/dcim/tables/devicetypes.py b/netbox/dcim/tables/devicetypes.py index c3064e7cd..2da9daee7 100644 --- a/netbox/dcim/tables/devicetypes.py +++ b/netbox/dcim/tables/devicetypes.py @@ -43,7 +43,7 @@ class ManufacturerTable(NetBoxTable): verbose_name='Platforms' ) slug = tables.Column() - contacts = tables.ManyToManyColumn( + contacts = columns.ManyToManyColumn( linkify_item=True ) tags = columns.TagColumn( diff --git a/netbox/dcim/tables/power.py b/netbox/dcim/tables/power.py index cab95bb02..92c4bb0aa 100644 --- a/netbox/dcim/tables/power.py +++ b/netbox/dcim/tables/power.py @@ -26,7 +26,7 @@ class PowerPanelTable(NetBoxTable): url_params={'power_panel_id': 'pk'}, verbose_name='Feeds' ) - contacts = tables.ManyToManyColumn( + contacts = columns.ManyToManyColumn( linkify_item=True ) tags = columns.TagColumn( diff --git a/netbox/dcim/tables/racks.py b/netbox/dcim/tables/racks.py index e5a1c8488..e6368cb74 100644 --- a/netbox/dcim/tables/racks.py +++ b/netbox/dcim/tables/racks.py @@ -69,7 +69,7 @@ class RackTable(NetBoxTable): orderable=False, verbose_name='Power' ) - contacts = tables.ManyToManyColumn( + contacts = columns.ManyToManyColumn( linkify_item=True ) tags = columns.TagColumn( diff --git a/netbox/dcim/tables/sites.py b/netbox/dcim/tables/sites.py index 84522480f..fa3c73e12 100644 --- a/netbox/dcim/tables/sites.py +++ b/netbox/dcim/tables/sites.py @@ -26,7 +26,7 @@ class RegionTable(NetBoxTable): url_params={'region_id': 'pk'}, verbose_name='Sites' ) - contacts = tables.ManyToManyColumn( + contacts = columns.ManyToManyColumn( linkify_item=True ) tags = columns.TagColumn( @@ -55,7 +55,7 @@ class SiteGroupTable(NetBoxTable): url_params={'group_id': 'pk'}, verbose_name='Sites' ) - contacts = tables.ManyToManyColumn( + contacts = columns.ManyToManyColumn( linkify_item=True ) tags = columns.TagColumn( @@ -86,7 +86,7 @@ class SiteTable(NetBoxTable): group = tables.Column( linkify=True ) - asns = tables.ManyToManyColumn( + asns = columns.ManyToManyColumn( linkify_item=True, verbose_name='ASNs' ) @@ -98,7 +98,7 @@ class SiteTable(NetBoxTable): ) tenant = TenantColumn() comments = columns.MarkdownColumn() - contacts = tables.ManyToManyColumn( + contacts = columns.ManyToManyColumn( linkify_item=True ) tags = columns.TagColumn( @@ -137,7 +137,7 @@ class LocationTable(NetBoxTable): url_params={'location_id': 'pk'}, verbose_name='Devices' ) - contacts = tables.ManyToManyColumn( + contacts = columns.ManyToManyColumn( linkify_item=True ) tags = columns.TagColumn( diff --git a/netbox/ipam/tables/ip.py b/netbox/ipam/tables/ip.py index 244bcee8e..475ad787e 100644 --- a/netbox/ipam/tables/ip.py +++ b/netbox/ipam/tables/ip.py @@ -118,7 +118,7 @@ class ASNTable(NetBoxTable): url_params={'asn_id': 'pk'}, verbose_name='Provider Count' ) - sites = tables.ManyToManyColumn( + sites = columns.ManyToManyColumn( linkify_item=True, verbose_name='Sites' ) diff --git a/netbox/netbox/tables/columns.py b/netbox/netbox/tables/columns.py index ba5583a2e..801b97766 100644 --- a/netbox/netbox/tables/columns.py +++ b/netbox/netbox/tables/columns.py @@ -6,7 +6,7 @@ from django.conf import settings from django.contrib.auth.models import AnonymousUser from django.db.models import DateField, DateTimeField from django.template import Context, Template -from django.urls import NoReverseMatch, reverse +from django.urls import reverse from django.utils.formats import date_format from django.utils.safestring import mark_safe from django_tables2.columns import library @@ -27,6 +27,7 @@ __all__ = ( 'CustomLinkColumn', 'LinkedCountColumn', 'MarkdownColumn', + 'ManyToManyColumn', 'MPTTColumn', 'TagColumn', 'TemplateColumn', @@ -35,6 +36,10 @@ __all__ = ( ) +# +# Django-tables2 overrides +# + @library.register class DateColumn(tables.DateColumn): """ @@ -42,7 +47,6 @@ class DateColumn(tables.DateColumn): tables and null when exporting data. It is registered in the tables library to use this class instead of the default, making this behavior consistent in all fields of type DateField. """ - def value(self, value): return value @@ -59,7 +63,6 @@ class DateTimeColumn(tables.DateTimeColumn): tables and null when exporting data. It is registered in the tables library to use this class instead of the default, making this behavior consistent in all fields of type DateTimeField. """ - def value(self, value): if value: return date_format(value, format="SHORT_DATETIME_FORMAT") @@ -71,6 +74,39 @@ class DateTimeColumn(tables.DateTimeColumn): return cls(**kwargs) +class ManyToManyColumn(tables.ManyToManyColumn): + """ + Overrides django-tables2's stock ManyToManyColumn to ensure that value() returns only plaintext data. + """ + def value(self, value): + items = [self.transform(item) for item in self.filter(value)] + return self.separator.join(items) + + +class TemplateColumn(tables.TemplateColumn): + """ + Overrides django-tables2's stock TemplateColumn class to render a placeholder symbol if the returned value + is an empty string. + """ + PLACEHOLDER = mark_safe('—') + + def render(self, *args, **kwargs): + ret = super().render(*args, **kwargs) + if not ret.strip(): + return self.PLACEHOLDER + return ret + + def value(self, **kwargs): + ret = super().value(**kwargs) + if ret == self.PLACEHOLDER: + return '' + return ret + + +# +# Custom columns +# + class ToggleColumn(tables.CheckBoxColumn): """ Extend CheckBoxColumn to add a "toggle all" checkbox in the column header. @@ -112,26 +148,6 @@ class BooleanColumn(tables.Column): return str(value) -class TemplateColumn(tables.TemplateColumn): - """ - Overrides django-tables2's stock TemplateColumn class to render a placeholder symbol if the returned value - is an empty string. - """ - PLACEHOLDER = mark_safe('—') - - def render(self, *args, **kwargs): - ret = super().render(*args, **kwargs) - if not ret.strip(): - return self.PLACEHOLDER - return ret - - def value(self, **kwargs): - ret = super().value(**kwargs) - if ret == self.PLACEHOLDER: - return '' - return ret - - @dataclass class ActionsItem: title: str diff --git a/netbox/tenancy/tables/tenants.py b/netbox/tenancy/tables/tenants.py index 5577d90e0..8f18423be 100644 --- a/netbox/tenancy/tables/tenants.py +++ b/netbox/tenancy/tables/tenants.py @@ -38,7 +38,7 @@ class TenantTable(NetBoxTable): linkify=True ) comments = columns.MarkdownColumn() - contacts = tables.ManyToManyColumn( + contacts = columns.ManyToManyColumn( linkify_item=True ) tags = columns.TagColumn( diff --git a/netbox/virtualization/tables/clusters.py b/netbox/virtualization/tables/clusters.py index c9f87105d..a0c98425a 100644 --- a/netbox/virtualization/tables/clusters.py +++ b/netbox/virtualization/tables/clusters.py @@ -40,7 +40,7 @@ class ClusterGroupTable(NetBoxTable): url_params={'group_id': 'pk'}, verbose_name='Clusters' ) - contacts = tables.ManyToManyColumn( + contacts = columns.ManyToManyColumn( linkify_item=True ) tags = columns.TagColumn( @@ -83,7 +83,7 @@ class ClusterTable(NetBoxTable): verbose_name='VMs' ) comments = columns.MarkdownColumn() - contacts = tables.ManyToManyColumn( + contacts = columns.ManyToManyColumn( linkify_item=True ) tags = columns.TagColumn( diff --git a/netbox/virtualization/tables/virtualmachines.py b/netbox/virtualization/tables/virtualmachines.py index d5017eb53..89dbdf901 100644 --- a/netbox/virtualization/tables/virtualmachines.py +++ b/netbox/virtualization/tables/virtualmachines.py @@ -78,7 +78,7 @@ class VMInterfaceTable(BaseInterfaceTable): vrf = tables.Column( linkify=True ) - contacts = tables.ManyToManyColumn( + contacts = columns.ManyToManyColumn( linkify_item=True ) tags = columns.TagColumn(