diff --git a/netbox/netbox/views/generic.py b/netbox/netbox/views/generic.py index af09dd546..25ef7dc50 100644 --- a/netbox/netbox/views/generic.py +++ b/netbox/netbox/views/generic.py @@ -15,6 +15,7 @@ from django.utils.http import is_safe_url from django.utils.safestring import mark_safe from django.views.generic import View from django_tables2 import RequestConfig +from django_tables2.export import TableExport from extras.models import CustomField, ExportTemplate from utilities.error_handlers import handle_protectederror @@ -137,32 +138,35 @@ class ObjectListView(ObjectPermissionRequiredMixin, View): if self.filterset: self.queryset = self.filterset(request.GET, self.queryset).qs - # Check for export template rendering - if request.GET.get('export'): - et = get_object_or_404(ExportTemplate, content_type=content_type, name=request.GET.get('export')) - try: - return et.render_to_response(self.queryset) - except Exception as e: - messages.error( - request, - "There was an error rendering the selected export template ({}): {}".format( - et.name, e + # Check for export rendering (except for table-based) + if 'export' in request.GET and request.GET['export'] != 'table': + + # An export template has been specified + if request.GET['export']: + et = get_object_or_404(ExportTemplate, content_type=content_type, name=request.GET['export']) + try: + return et.render_to_response(self.queryset) + except Exception as e: + messages.error( + request, + "There was an error rendering the selected export template ({}): {}".format( + et.name, e + ) ) - ) - # Check for YAML export support - elif 'export' in request.GET and hasattr(model, 'to_yaml'): - response = HttpResponse(self.queryset_to_yaml(), content_type='text/yaml') - filename = 'netbox_{}.yaml'.format(self.queryset.model._meta.verbose_name_plural) - response['Content-Disposition'] = 'attachment; filename="{}"'.format(filename) - return response + # Check for YAML export support + elif hasattr(model, 'to_yaml'): + response = HttpResponse(self.queryset_to_yaml(), content_type='text/yaml') + filename = 'netbox_{}.yaml'.format(self.queryset.model._meta.verbose_name_plural) + response['Content-Disposition'] = 'attachment; filename="{}"'.format(filename) + return response - # Fall back to built-in CSV formatting if export requested but no template specified - elif 'export' in request.GET and hasattr(model, 'to_csv'): - response = HttpResponse(self.queryset_to_csv(), content_type='text/csv') - filename = 'netbox_{}.csv'.format(self.queryset.model._meta.verbose_name_plural) - response['Content-Disposition'] = 'attachment; filename="{}"'.format(filename) - return response + # Fall back to built-in CSV formatting if export requested but no template specified + elif 'export' in request.GET and hasattr(model, 'to_csv'): + response = HttpResponse(self.queryset_to_csv(), content_type='text/csv') + filename = 'netbox_{}.csv'.format(self.queryset.model._meta.verbose_name_plural) + response['Content-Disposition'] = 'attachment; filename="{}"'.format(filename) + return response # Compile a dictionary indicating which permissions are available to the current user for this model permissions = {} @@ -175,6 +179,18 @@ class ObjectListView(ObjectPermissionRequiredMixin, View): if 'pk' in table.base_columns and (permissions['change'] or permissions['delete']): table.columns.show('pk') + # Handle table-based export + if request.GET.get('export') == 'table': + exporter = TableExport( + export_format=TableExport.CSV, + table=table, + exclude_columns=['pk'], + dataset_kwargs={}, + ) + return exporter.response( + filename=f'netbox_{self.queryset.model._meta.verbose_name_plural}.csv' + ) + # Apply the request context paginate = { 'paginator_class': EnhancedPaginator, diff --git a/netbox/utilities/templates/buttons/export.html b/netbox/utilities/templates/buttons/export.html index a6ec52d1f..341dca3f0 100644 --- a/netbox/utilities/templates/buttons/export.html +++ b/netbox/utilities/templates/buttons/export.html @@ -1,19 +1,16 @@ -{% if export_templates %} -