NetBox是IP地址管理(IPAM)和数据中心基础结构管理(DCIM)工具。 NetBox最初是由的网络工程团队构想的,它是专门为满足网络和基础架构工程师的需求而开发的。
项目地址:https://github.com/netbox-community/netbox
修改文件:./netbox/netbox/netbox/views/generic.py
修改内容:
# 1. 在 import 语句后面添加一个新的类,重写父类 `TableExport` ... class TableExport2(TableExport): def response(self, filename=None): response = HttpResponse(content_type="text/csv; charset=utf-8-sig") if filename is not None: response["Content-Disposition"] = 'attachment; filename="{}"'.format(filename) response.write(self.export()) return response # 2. 修改 export_table 方法(大约在文件 200 行左右)让其使用重写的类来实例化 class ObjectListView(ObjectPermissionRequiredMixin, View): ... # def export_table(self, table, columns=None): ... exporter = TableExport2( export_format=TableExport.CSV, table=table, exclude_columns=exclude_columns ) ...
修改文件后,重启 netbox 服务。如果是容器部署,重启 netbox-docker_netbox_1
容器(暴露端口的那个容器)。
Django 导出 csv 文件乱码,一般是编码格式的问题,可以参考:解决 Django 导出 csv 乱码问题,根据这篇文章,思路就是只需要修改 HttpResponse
的 charset
就行了。
在 url.py 中搜索 export 相关字样,发现没有相关路由。
在从 export 的 html 中可以看到,它是直接把 export 传给了后端。
<li><a class="dropdown-item" href="?{% if url_params %}{{ url_params.urlencode }}&{% endif %}export">All
后端处理的时候肯定是需要解析这个字段的,在 views.py 中搜索,发现定义了 export 的按钮,是继承自 generic.ObjectListView
的,所以这个方法应该是定义在 generic.ObjectListView
中。
class ConsolePortListView(generic.ObjectListView): queryset = ConsolePort.objects.all() filterset = filtersets.ConsolePortFilterSet filterset_form = forms.ConsolePortFilterForm table = tables.ConsolePortTable action_buttons = ('import', 'export')
在 generic 中找到如下方法,证明前端 export 传过来的参数是由 export_table
来处理:
... if 'export' in request.GET: # Export the current table view if request.GET['export'] == 'table': table = self.get_table(request, permissions) columns = [name for name, _ in table.selected_columns] return self.export_table(table, columns) ... def export_table(self, table, columns=None): """ Export all table data in CSV format. :param table: The Table instance to export :param columns: A list of specific columns to include. If not specified, all columns will be exported. """ exclude_columns = {'pk'} if columns: all_columns = [col_name for col_name, _ in table.selected_columns + table.available_columns] exclude_columns.update({ col for col in all_columns if col not in columns }) exporter = TableExport( export_format=TableExport.CSV, table=table, exclude_columns=exclude_columns ) return exporter.response( filename=f'netbox_{self.queryset.model._meta.verbose_name_plural}.csv' )
可以看到这个方法返回了一个 response
对象,修改这个对象的 charset:
def export_table(self, table, columns=None): ... response = exporter.response( filename=f'netbox_{self.queryset.model._meta.verbose_name_plural}.csv' ) response.charset = "utf-8-sig" return response
修改之后,发现前端页面中,确实增加了 charset 且值正确,但是导出的文件仍为乱码。
分析 exporter.response
这个对象——来自于 django-tables2
插件,初始化 TableExport
时里面使用了 export_format=TableExport.CSV
,这里指定了 csv 格式
exporter = TableExport( export_format=TableExport.CSV, table=table, exclude_columns=exclude_columns )
之后调用了这个类的 response
方法:
def response(self, filename=None): response = HttpResponse(content_type=self.content_type()) if filename is not None: response["Content-Disposition"] = 'attachment; filename="{}"'.format(filename) response.write(self.export()) return
通过分析这个类,发现这里 self.content_type() 调用的是传入的 self.format,即 export_format=TableExport.CSV
值为 ‘csv’。
由于这个值会被 tablib
这个库调用(类中的 self.export()方法调用),所以不能重写这个字段来实现。
response
方法也不支持传参,所以这里的 csv 编码格式是写死的,即:"text/csv; charset=utf-8"
而且 response.write(self.export())
写入对象时已经是 utf-8 了,写入完成之后再修改编码也没有任何意义。
最后思考的结果是:在 netbox 的 generic.py
中重写 def response(self, filename=None):
方法,固定其编码为 utf-8-sig
。
因为 netbox 的导出只使用了 csv 格式,所以把编码格式固定不会对 netbox 运行造成影响,只是修改了导出的格式。
最终解决方法见上方。
修改文件: ./netbox-docker/configuration/extra.py