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 665bfb99e93..7b9a9e4b2b4 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 cb8c940b001..40f8918ae67 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 e97ade7d87c..0ec6d439d00 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 25ad1415de7..0f015b7f34c 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 c3064e7cd7d..2da9daee75a 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 cab95bb02b2..92c4bb0aa22 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 e5a1c8488bd..e6368cb745b 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 84522480fa0..fa3c73e124d 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 244bcee8e37..475ad787e6e 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 ba5583a2e9c..801b9776698 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 5577d90e051..8f18423be89 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 c9f87105dc9..a0c98425a23 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 d5017eb5371..89dbdf901be 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(