当我们需要用django的中文界面时,可以设置LANGUAGE_CODE = 'zh-hans'
在admin页面需要展示列表字段为中文名时,可以在模型中为各字段添加verbose_name。
而在使用导出功能中发现,导出的excel,csv表格中列名会默认成模型中的英文字段名。
为了更易读,现在将表格中的列名改为字段的verbose_name。
1. 导出表格中的列名显示
首先,我们先在DeviceResource的init中新建一个全局字段vname_dict,获取Device模型中的所有字段对应的verbose_name。
#myproject/admin.py
class DeviceResource(resources.ModelResource):
...
def __init__(self):
super(DeviceResource, self).__init__()
# 获取所以字段的verbose_name并存放在字典
field_list = apps.get_model('deviceManager', 'Device')._meta.fields
self.vname_dict = {}
for i in field_list:
self.vname_dict[i.name] = i.verbose_name
然后,查看resource.py源码,可以发现导出数据前,是通过get_export_fields方法获取要导出的数据,而该方法其实只是直接调用了get_fields方法:获取数据并按export_order的顺序排列,返回一个列表。
# import-export/resource.py
...
def get_export_fields(self):
return self.get_fields()
我们直接重写get_export_fields方法,在这里修改导出数据的列名就可以了。
# myapp/admin.py
# 默认导入导出field的column_name为字段的名称,这里修改为字段的verbose_name
def get_export_fields(self):
fields = self.get_fields()
for field in fields:
field_name = self.get_field_name(field)
# 如果我们设置过verbose_name,则将column_name替换为verbose_name。否则维持原有的字段名
if field_name in self.vname_dict.keys():
field.column_name = self.vname_dict[field_name]
return fields
再使用导入导出时,列名就变为我们为模型中字段设置的verbose_name啦!
2. 导出表格中的外键显示
接着发现一个问题:我们导出的表格中,所有的外键字段会显示外键的id,而不是我们想看到的文字。查看resource.py源文件,发现有一个before_export方法,官方注释写着可以重写这个方法去自定义一些导出前的操作。但是,我发现before_export中只能获取到queryset,也是就从数据库查询后的一个列表,是不可更改的,我根本没法在这里修改将要导出的数据。于是只能重写export方法了。
首先,我们需要知道哪些字段是外键。在DeviceResource的init方法中再新建一个全局变量self.fkey。现在,init变为:
#myproject/admin.py
class DeviceResource(resources.ModelResource):
...
def __init__(self):
super(DeviceResource, self).__init__()
#获取deviceManager应用下Device模型中的所有字段
field_list = apps.get_model('deviceManager', 'Device')._meta.fields
self.vname_dict = {}
self.fkey = []
for i in field_list:
self.vname_dict[i.name] = i.verbose_name # 获取所有字段的verbose_name并存放在字典
if(isinstance(i, ForeignKey)):
self.fkey.append(i.name) # 获取所有ForeignKey字段的name存放在列表
然后,把resource.py中的export方法全部拷贝过来后进行修改,如下。
备注: # ---------- #之间的是自己添加的代码,其它为export原本的代码。
#myproject/admin.py
class DeviceResource(resources.ModelResource):
...
# 重载resources.py的export方法,修改将要导出的data的某些外键相关数据。默认导出外键id,这里修改为导出外键对应的值
def export(self, queryset=None, *args, **kwargs):
self.before_export(queryset, *args, **kwargs)
if queryset is None:
queryset = self.get_queryset()
headers = self.get_export_headers()
data = tablib.Dataset(headers=headers)
# --------------------- #
# 获取所有外键名称在headers中的位置
fk_index = {}
for fk in self.fkey:
fk_index[fk] = headers.index(self.vname_dict[fk])
# --------------------- #
if isinstance(queryset, QuerySet):
# Iterate without the queryset cache, to avoid wasting memory when
# exporting large datasets.
iterable = queryset.iterator()
else:
iterable = queryset
for obj in iterable:
# --------------------- #
# 获取将要导出的源数据,这里export_resource返回的是列表,便于更改。替换到外键的值
res = self.export_resource(obj)
res[fk_index['area_company']] = Group.objects.get(id=res[fk_index['area_company']]).name
res[fk_index['site']] = Site.objects.get(id=res[fk_index['site']]).site
res[fk_index['device_type']] = DeviceType.objects.get(id=res[fk_index['device_type']]).type
res[fk_index['device_model']] = Model.objects.get(id=res[fk_index['device_model']]).model
res[fk_index['supplier']] = Supplier.objects.get(id=res[fk_index['supplier']]).supplier
data.append(res)
# --------------------- #
self.after_export(queryset, data, *args, **kwargs)
return data
这样,导出表格中的外键字段就不会再显示为id啦。对比如下:
修改前:
修改后:
3. 导入时外键字段的转换
对于外键的处理,导入时存在同样的问题。我们自己填写表格时,肯定不会填写这个值对应在另一个数据表里的id。所以执行导入操作前,我们也需要将数据源中实际输入的值转换为对应的外键id。
查看resource.py源码,发现我们可以直接重写before_import方法去修改数据源dataset,代码如下。
#myproject/admin.py
class DeviceResource(resources.ModelResource):
...
# import相关。将导入的表格中的一些文字通过数据库查询一些外键field对应的id。也可通过设置use_transactions=True,重写before_save_instance等方法
def before_import(self, dataset, using_transactions, dry_run, **kwargs):
dict = []
id = Device.objects.latest('id').id+1
for row in dataset.dict:
tmp = OrderedDict()
tmp['ID'] = id
for item in row:
if item == self.vname_dict['area_company']:
tmp[item] = Group.objects.get(name=row[self.vname_dict['area_company']]).id
elif item == self.vname_dict['site']:
tmp[item] = Site.objects.get(site=row[self.vname_dict['site']]).id
elif item == self.vname_dict['device_type']:
tmp[item] = DeviceType.objects.get(type=row[self.vname_dict['device_type']]).id
elif item == self.vname_dict['device_model']:
tmp[item] = Model.objects.get(model=row[self.vname_dict['device_model']]).id
elif item == self.vname_dict['supplier']:
tmp[item] = Supplier.objects.get(supplier=row[self.vname_dict['supplier']]).id
else:
tmp[item] = row[item]
id = id+1
dict.append(tmp)
dataset.dict = dict
修改前,如果导入外键字段不是填int的表格,会报错。例如,如果导入以下表格:
会出现如下报错:
修改后:
导入导出功能的优化就搞定啦!