當(dāng)我們需要用django的中文界面時(shí)粉渠,可以設(shè)置LANGUAGE_CODE = 'zh-hans'
在admin頁(yè)面需要展示列表字段為中文名時(shí),可以在模型中為各字段添加verbose_name派哲。
而在使用導(dǎo)出功能中發(fā)現(xiàn),導(dǎo)出的excel掺喻,csv表格中列名會(huì)默認(rèn)成模型中的英文字段名芭届。
為了更易讀,現(xiàn)在將表格中的列名改為字段的verbose_name感耙。
1. 導(dǎo)出表格中的列名顯示
首先喉脖,我們先在DeviceResource的init中新建一個(gè)全局字段vname_dict,獲取Device模型中的所有字段對(duì)應(yīng)的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源碼舆蝴,可以發(fā)現(xiàn)導(dǎo)出數(shù)據(jù)前谦絮,是通過get_export_fields方法獲取要導(dǎo)出的數(shù)據(jù),而該方法其實(shí)只是直接調(diào)用了get_fields方法:獲取數(shù)據(jù)并按export_order的順序排列洁仗,返回一個(gè)列表层皱。
# import-export/resource.py
...
def get_export_fields(self):
return self.get_fields()
我們直接重寫get_export_fields方法,在這里修改導(dǎo)出數(shù)據(jù)的列名就可以了赠潦。
# myapp/admin.py
# 默認(rèn)導(dǎo)入導(dǎo)出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)
# 如果我們?cè)O(shè)置過verbose_name,則將column_name替換為verbose_name她奥。否則維持原有的字段名
if field_name in self.vname_dict.keys():
field.column_name = self.vname_dict[field_name]
return fields
再使用導(dǎo)入導(dǎo)出時(shí)瓮增,列名就變?yōu)槲覀優(yōu)槟P椭凶侄卧O(shè)置的verbose_name啦!
2. 導(dǎo)出表格中的外鍵顯示
接著發(fā)現(xiàn)一個(gè)問題:我們導(dǎo)出的表格中哩俭,所有的外鍵字段會(huì)顯示外鍵的id绷跑,而不是我們想看到的文字。查看resource.py源文件凡资,發(fā)現(xiàn)有一個(gè)before_export方法砸捏,官方注釋寫著可以重寫這個(gè)方法去自定義一些導(dǎo)出前的操作。但是,我發(fā)現(xiàn)before_export中只能獲取到queryset垦藏,也是就從數(shù)據(jù)庫(kù)查詢后的一個(gè)列表梆暖,是不可更改的,我根本沒法在這里修改將要導(dǎo)出的數(shù)據(jù)掂骏。于是只能重寫export方法了轰驳。
首先,我們需要知道哪些字段是外鍵芭挽。在DeviceResource的init方法中再新建一個(gè)全局變量self.fkey』希現(xiàn)在,init變?yōu)椋?/p>
#myproject/admin.py
class DeviceResource(resources.ModelResource):
...
def __init__(self):
super(DeviceResource, self).__init__()
#獲取deviceManager應(yīng)用下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方法全部拷貝過來(lái)后進(jìn)行修改蠕趁,如下。
備注: # ---------- #之間的是自己添加的代碼辛馆,其它為export原本的代碼俺陋。
#myproject/admin.py
class DeviceResource(resources.ModelResource):
...
# 重載resources.py的export方法,修改將要導(dǎo)出的data的某些外鍵相關(guān)數(shù)據(jù)昙篙。默認(rèn)導(dǎo)出外鍵id腊状,這里修改為導(dǎo)出外鍵對(duì)應(yīng)的值
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:
# --------------------- #
# 獲取將要導(dǎo)出的源數(shù)據(jù),這里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
這樣,導(dǎo)出表格中的外鍵字段就不會(huì)再顯示為id啦焚辅。對(duì)比如下:
修改前:
修改后:
3. 導(dǎo)入時(shí)外鍵字段的轉(zhuǎn)換
對(duì)于外鍵的處理映屋,導(dǎo)入時(shí)存在同樣的問題。我們自己填寫表格時(shí)同蜻,肯定不會(huì)填寫這個(gè)值對(duì)應(yīng)在另一個(gè)數(shù)據(jù)表里的id棚点。所以執(zhí)行導(dǎo)入操作前,我們也需要將數(shù)據(jù)源中實(shí)際輸入的值轉(zhuǎn)換為對(duì)應(yīng)的外鍵id湾蔓。
查看resource.py源碼瘫析,發(fā)現(xiàn)我們可以直接重寫before_import方法去修改數(shù)據(jù)源dataset,代碼如下默责。
#myproject/admin.py
class DeviceResource(resources.ModelResource):
...
# import相關(guān)贬循。將導(dǎo)入的表格中的一些文字通過數(shù)據(jù)庫(kù)查詢一些外鍵field對(duì)應(yīng)的id。也可通過設(shè)置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
修改前桃序,如果導(dǎo)入外鍵字段不是填int的表格甘有,會(huì)報(bào)錯(cuò)。例如葡缰,如果導(dǎo)入以下表格:
會(huì)出現(xiàn)如下報(bào)錯(cuò):
修改后:
導(dǎo)入導(dǎo)出功能的優(yōu)化就搞定啦亏掀!