django-import-export关联外键在后台管理中导入导出的办法
本文介绍django-import-export在关联外键时,导出可以导出可读字样(而非关联id),导入通过可读字眼进行导入(而非关联id)
模型设计如下:
# models.py
from django.db import models
# Create your models here.
class User(models.Model):
gender_by_choice = (
('male', '男'),
('female', '女'),
)
username = models.CharField(max_length=10, unique=True, verbose_name='姓名')
age = models.IntegerField(verbose_name='年龄')
gender = models.CharField(max_length=6, choices=gender_by_choice, verbose_name='性别')
nickname = models.CharField(max_length=10, verbose_name='昵称')
def __str__(self):
return self.username
class Meta:
verbose_name = '用户'
verbose_name_plural = '用户'
class Book(models.Model):
owner = models.ForeignKey(User, null=True, blank=True, on_delete=models.SET_NULL, verbose_name='归属者')
name = models.CharField(max_length=64, verbose_name='书名')
def __str__(self):
return self.name
class Meta:
verbose_name = '书'
verbose_name_plural = '书'
可以看到上述的Book模型中的owner关联了User表,先按照django-import-export的官网所述的方法配置,看看会遇到什么问题
- 配置admin.py
# admin.py
from import_export import resources
from import_export.admin import ImportExportModelAdmin
class BookResource(resources.ModelResource):
class Meta:
model = Book
class BookAdmin(ImportExportModelAdmin):
resource_class = BookResource
# 这里加几个显示字段
list_display = ['name', 'owner']
class UserResource(resources.ModelResource):
class Meta:
model = User
class UserAdmin(ImportExportModelAdmin):
resource_class = UserResource
# 这里加几个显示字段
list_display = ['username', 'age', 'gender', 'nickname']
admin.site.register(Book, BookAdmin)
admin.site.register(User, UserAdmin)
如下图,右上角已经有了导入导出按钮,django-import-export已经加载成功
录入两条用户数据和三条书籍信息,如下图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UFcspVy1-1583570587234)(https://upload-images.jianshu.io/upload_images/15925527-b18310a89989a753.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wtdqkOWE-1583570587235)(https://upload-images.jianshu.io/upload_images/15925527-da85dedaec711212.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]
如果将书籍导出,会得到如下效果,owner会显示user表的id信息,并不是我们想要的结果:
导入会有同样的问题,导入时owner中的值必须是user中的id,而不能是张三、李四,所以我们需要对导入和导出做一些调整,以实现我们的需求:
from django.contrib import admin
from .models import Book, User
from import_export import resources
from django.apps import apps
from import_export.admin import ImportExportModelAdmin
from django.db import models
import tablib
import collections
class BookResource(resources.ModelResource):
def __init__(self):
super(BookResource, self).__init__()
# 获取tables应用下Book模型中的所有字段,请根据自己的应用将tables更改
field_list = apps.get_model('tables', 'Book')._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, models.ForeignKey)):
self.fkey.append(i.name) # 获取所有ForeignKey字段的name存放在列表
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(fk)
iterable = queryset
for obj in iterable:
# 获取将要导出的源数据,这里export_resource返回的是列表,便于更改。替换到外键的值
res = self.export_resource(obj)
"""
这里是关键,将owner的值到User中获取对应的对象,并截取起可读名称username,
这里用的是get,所以在User的模型中username必须是unique
"""
res[fk_index['owner']] = User.objects.get(id=res[fk_index['owner']]).username
data.append(res)
self.after_export(queryset, data, *args, **kwargs)
return data
def before_import(self, dataset, using_transactions, dry_run, **kwargs):
dict = []
for row in dataset.dict:
tmp = collections.OrderedDict()
books = Book.objects.all()
for item in row:
if item == 'owner':
"""
这里是关键,通过可读名称到User表中找到对应id,并加到导入的数据中去
"""
tmp[item] = User.objects.get(username=row[item]).id
else:
tmp[item] = row[item]
"""
这里是关键,将数据进行比对,如果数据相同,就把原先在Book表中的id加到需要导入的数据中去,
这样就不会新增和原先一模一样的数据,类似于create_or_update方法
"""
for book in books:
if row['name'] == book.name:
tmp['id'] = book.id
dict.append(tmp)
dataset.dict = dict
return dataset
class Meta:
model = Book
class BookAdmin(ImportExportModelAdmin):
resource_class = BookResource
class BookAdmin(ImportExportModelAdmin):
resource_class = BookResource
list_display = ['name', 'owner']
class UserResource(resources.ModelResource):
class Meta:
model = User
class UserAdmin(ImportExportModelAdmin):
resource_class = UserResource
list_display = ['username', 'age', 'gender', 'nickname']
admin.site.register(Book, BookAdmin)
admin.site.register(User, UserAdmin)
请特别注意,关联查询的字段应该在模型中设定unique=True,如果不唯一,在User表中可能会查到多条数据,这样Book表是不知道要关联那条记录的
将书籍导出后的效果如下:
再来说导入,导入的表如下表:
提交后的校验界面如下:
确认导入后,数据如下:
上一篇: Lock接口