mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-16 04:02:52 -06:00
* Closes #19739: Add a user preference for CSV delimiter in table exports * Pass custom delimiter when exporting entire table
This commit is contained in:
parent
a1cd81ff35
commit
90e8a61670
@ -44,3 +44,10 @@ CENSOR_TOKEN_CHANGED = '***CHANGED***'
|
|||||||
|
|
||||||
# Placeholder text for empty tables
|
# Placeholder text for empty tables
|
||||||
EMPTY_TABLE_TEXT = 'No results found'
|
EMPTY_TABLE_TEXT = 'No results found'
|
||||||
|
|
||||||
|
# CSV delimiters
|
||||||
|
CSV_DELIMITERS = {
|
||||||
|
'comma': ',',
|
||||||
|
'semicolon': ';',
|
||||||
|
'pipe': '|',
|
||||||
|
}
|
||||||
|
@ -72,6 +72,16 @@ PREFERENCES = {
|
|||||||
),
|
),
|
||||||
description=_('The preferred syntax for displaying generic data within the UI')
|
description=_('The preferred syntax for displaying generic data within the UI')
|
||||||
),
|
),
|
||||||
|
'csv_delimiter': UserPreference(
|
||||||
|
label=_('CSV delimiter'),
|
||||||
|
choices=(
|
||||||
|
('comma', 'Comma (,)'),
|
||||||
|
('semicolon', 'Semicolon (;)'),
|
||||||
|
('pipe', 'Pipe (|)'),
|
||||||
|
),
|
||||||
|
default='comma',
|
||||||
|
description=_('The character used to separate fields in CSV data')
|
||||||
|
),
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,6 @@ from django.shortcuts import get_object_or_404, redirect, render
|
|||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
from django_tables2.export import TableExport
|
|
||||||
from mptt.models import MPTTModel
|
from mptt.models import MPTTModel
|
||||||
|
|
||||||
from core.models import ObjectType
|
from core.models import ObjectType
|
||||||
@ -25,6 +24,7 @@ from extras.models import CustomField, ExportTemplate
|
|||||||
from netbox.object_actions import AddObject, BulkDelete, BulkEdit, BulkExport, BulkImport, BulkRename
|
from netbox.object_actions import AddObject, BulkDelete, BulkEdit, BulkExport, BulkImport, BulkRename
|
||||||
from utilities.error_handlers import handle_protectederror
|
from utilities.error_handlers import handle_protectederror
|
||||||
from utilities.exceptions import AbortRequest, AbortTransaction, PermissionsViolation
|
from utilities.exceptions import AbortRequest, AbortTransaction, PermissionsViolation
|
||||||
|
from utilities.export import TableExport
|
||||||
from utilities.forms import BulkRenameForm, ConfirmationForm, restrict_form_fields
|
from utilities.forms import BulkRenameForm, ConfirmationForm, restrict_form_fields
|
||||||
from utilities.forms.bulk_import import BulkImportForm
|
from utilities.forms.bulk_import import BulkImportForm
|
||||||
from utilities.htmx import htmx_partial
|
from utilities.htmx import htmx_partial
|
||||||
@ -77,7 +77,7 @@ class ObjectListView(BaseMultiObjectView, ActionsMixin, TableMixin):
|
|||||||
|
|
||||||
return '---\n'.join(yaml_data)
|
return '---\n'.join(yaml_data)
|
||||||
|
|
||||||
def export_table(self, table, columns=None, filename=None):
|
def export_table(self, table, columns=None, filename=None, delimiter=None):
|
||||||
"""
|
"""
|
||||||
Export all table data in CSV format.
|
Export all table data in CSV format.
|
||||||
|
|
||||||
@ -86,6 +86,7 @@ class ObjectListView(BaseMultiObjectView, ActionsMixin, TableMixin):
|
|||||||
columns: A list of specific columns to include. If None, all columns will be exported.
|
columns: A list of specific columns to include. If None, all columns will be exported.
|
||||||
filename: The name of the file attachment sent to the client. If None, will be determined automatically
|
filename: The name of the file attachment sent to the client. If None, will be determined automatically
|
||||||
from the queryset model name.
|
from the queryset model name.
|
||||||
|
delimiter: The character used to separate columns (a comma is used by default)
|
||||||
"""
|
"""
|
||||||
exclude_columns = {'pk', 'actions'}
|
exclude_columns = {'pk', 'actions'}
|
||||||
if columns:
|
if columns:
|
||||||
@ -96,7 +97,8 @@ class ObjectListView(BaseMultiObjectView, ActionsMixin, TableMixin):
|
|||||||
exporter = TableExport(
|
exporter = TableExport(
|
||||||
export_format=TableExport.CSV,
|
export_format=TableExport.CSV,
|
||||||
table=table,
|
table=table,
|
||||||
exclude_columns=exclude_columns
|
exclude_columns=exclude_columns,
|
||||||
|
delimiter=delimiter,
|
||||||
)
|
)
|
||||||
return exporter.response(
|
return exporter.response(
|
||||||
filename=filename or f'netbox_{self.queryset.model._meta.verbose_name_plural}.csv'
|
filename=filename or f'netbox_{self.queryset.model._meta.verbose_name_plural}.csv'
|
||||||
@ -159,7 +161,8 @@ class ObjectListView(BaseMultiObjectView, ActionsMixin, TableMixin):
|
|||||||
if request.GET['export'] == 'table':
|
if request.GET['export'] == 'table':
|
||||||
table = self.get_table(self.queryset, request, has_table_actions)
|
table = self.get_table(self.queryset, request, has_table_actions)
|
||||||
columns = [name for name, _ in table.selected_columns]
|
columns = [name for name, _ in table.selected_columns]
|
||||||
return self.export_table(table, columns)
|
delimiter = request.user.config.get('csv_delimiter')
|
||||||
|
return self.export_table(table, columns, delimiter=delimiter)
|
||||||
|
|
||||||
# Render an ExportTemplate
|
# Render an ExportTemplate
|
||||||
elif request.GET['export']:
|
elif request.GET['export']:
|
||||||
@ -176,7 +179,8 @@ class ObjectListView(BaseMultiObjectView, ActionsMixin, TableMixin):
|
|||||||
# Fall back to default table/YAML export
|
# Fall back to default table/YAML export
|
||||||
else:
|
else:
|
||||||
table = self.get_table(self.queryset, request, has_table_actions)
|
table = self.get_table(self.queryset, request, has_table_actions)
|
||||||
return self.export_table(table)
|
delimiter = request.user.config.get('csv_delimiter')
|
||||||
|
return self.export_table(table, delimiter=delimiter)
|
||||||
|
|
||||||
# Render the objects table
|
# Render the objects table
|
||||||
table = self.get_table(self.queryset, request, has_table_actions)
|
table = self.get_table(self.queryset, request, has_table_actions)
|
||||||
|
@ -62,7 +62,7 @@ class UserConfigForm(forms.ModelForm, metaclass=UserConfigFormMetaclass):
|
|||||||
'ui.tables.striping',
|
'ui.tables.striping',
|
||||||
name=_('User Interface')
|
name=_('User Interface')
|
||||||
),
|
),
|
||||||
FieldSet('data_format', name=_('Miscellaneous')),
|
FieldSet('data_format', 'csv_delimiter', name=_('Miscellaneous')),
|
||||||
)
|
)
|
||||||
# List of clearable preferences
|
# List of clearable preferences
|
||||||
pk = forms.MultipleChoiceField(
|
pk = forms.MultipleChoiceField(
|
||||||
|
26
netbox/utilities/export.py
Normal file
26
netbox/utilities/export.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from django_tables2.export import TableExport as TableExport_
|
||||||
|
|
||||||
|
from netbox.constants import CSV_DELIMITERS
|
||||||
|
|
||||||
|
__all__ = (
|
||||||
|
'TableExport',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TableExport(TableExport_):
|
||||||
|
"""
|
||||||
|
A subclass of django-tables2's TableExport class which allows us to specify a delimiting
|
||||||
|
characters for CSV exports.
|
||||||
|
"""
|
||||||
|
def __init__(self, *args, delimiter=None, **kwargs):
|
||||||
|
if delimiter and delimiter not in CSV_DELIMITERS.keys():
|
||||||
|
raise ValueError(_("Invalid delimiter name: {name}").format(name=delimiter))
|
||||||
|
self.delimiter = delimiter or 'comma'
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def export(self):
|
||||||
|
if self.format == self.CSV and self.delimiter is not None:
|
||||||
|
delimiter = CSV_DELIMITERS[self.delimiter]
|
||||||
|
return self.dataset.export(self.format, delimiter=delimiter)
|
||||||
|
return super().export()
|
Loading…
Reference in New Issue
Block a user