当前位置:网站首页 > 更多 > 涨姿势 > 正文

[每日一学] django ImportExportModelAdmin自定义导出数据

作者:CC下载站 日期:2020-09-23 00:00:00 浏览:69 分类:涨姿势


官方文档:django-import-export

django 导出数据到excel

借助官方文档的一些数据进行理解:

classAuthor(models.Model):
name=models.CharField(max_length=100)

def__str__(self):
returnself.name


classCategory(models.Model):
name=models.CharField(max_length=100)

def__str__(self):
returnself.name


classBook(models.Model):
name=models.CharField('Bookname',max_length=100)
author=models.ForeignKey(Author,blank=True,null=True)
author_email=models.EmailField('Authoremail',max_length=75,blank=True)
imported=models.BooleanField(default=False)
published=models.DateField('Published',blank=True,null=True)
price=models.DecimalField(max_digits=10,decimal_places=2,null=True,blank=True)
categories=models.ManyToManyField(Category,blank=True)

def__str__(self):
returnself.name

创建导入或导出的资源

fromimport_exportimportresources
fromcore.modelsimportBook

classBookResource(resources.ModelResource):

classMeta:
model=Book
#要影响哪些模型字段将包含在导入导出的资源中,请使用fields(自定义)选项将字段列入白名单
fields=('id','name','price',)
#或将exclude(自定义)字段列入黑名单的选项
exclude=('imported',)
#export_order(自定义)选项设置导出字段的显式顺序
export_order=('id','price','author','name')

现在已经创建了一个模型资源,我们可以导出到csv文件

>>>fromapp.adminimportBookResource
>>>dataset=BookResource().export()
>>>print(dataset.csv)
id,name,author,author_email,imported,published,price,categories
2,Somebook,1,,0,2012-12-05,8.85,1

定义ModelResource字段时,可以遵循模型关系,

classBookResource(resources.ModelResource):

classMeta:
model=Book
#注意这里是中间是两个下划线,表示链式调用的模型关系
fields=('author__name',)

定义ModelResource属性时,可以遵循模型关系,

fromimport_export.fieldsimportField
classBookResource(resources.ModelResource):
	'''自定义属性时,attribute所对应的值可以是链式调用,author__name实际得到的就是作者的名字,
	但是在查询时记得使用select_related(),以保证在查询时可以将所关联的外键数据一块查出,否则无法使用链式调用,
	最下面会有完整的例子说明
	'''
	author_name=Field(attribute='author__name',column_name='作者名字')
classMeta:
model=Book
#
fields=()
export_order=('id','price','author','name','author_name')

可以覆盖资源字段以更改其某些选项`

fromimport_export.fieldsimportField

classBookResource(resources.ModelResource):
published=Field(attribute='published',column_name='published_date')

classMeta:
model=Book

可以添加目标模型中不存在的其他字段

fromimport_export.fieldsimportField

classBookResource(resources.ModelResource):
myfield=Field(column_name='myfield',attribute='组织数据时自定义的名字')

classMeta:
model=Book

import_export.fields.Field可用的属性:

导出时进行高级数据处理:

'''
并非所有数据都可以从对象/模型属性中轻松提取。
为了在导出时将复杂的数据模型转换为(通常更简单)的已处理数据结构,dehydrate_<fieldname>应定义方法:
'''
fromimport_export.fieldsimportField

classBookResource(resources.ModelResource):
full_title=Field()

classMeta:
model=Book

defdehydrate_full_title(self,book):
return'%sby%s'%(book.name,book.author.name)

在这种情况下,导出看起来像这样:

>>>fromapp.adminimportBookResource
>>>dataset=BookResource().export()
>>>print(dataset.csv)
full_title,id,name,author,author_email,imported,published,price,categories
Somebookby1,2,Somebook,1,,0,2012-12-05,8.85,1

自定义小部件
一个ModelResource创建与给定字段类型的默认控件的字段。如果小部件应使用不同的参数初始化,请设置widgets字典。

在此示例窗口小部件中,该published字段被覆盖以使用其他日期格式。此格式将同时用于导入和导出资源。

classBookResource(resources.ModelResource):

classMeta:
model=Book
widgets={
'published':{'format':'%d.%m.%Y'},
}

应用场景:

视图是这样的:

渲染上面的视图主要用到了两个表:观看表和分享表

classWatchStatistics(models.Model):
"""
观看
"""
#支持匿名用户
user=models.ForeignKey(User,null=True)
course=models.ForeignKey(VideoInfo,related_name="watch_course")
ip=models.CharField(max_length=31)
duration=models.DecimalField(decimal_places=2,max_digits=12,
default=0.00,null=True)
createDate=models.DateTimeField(auto_now_add=True)
enterprise=models.ForeignKey(Enterprise,related_name="watch_enterprise")

startDate=models.DateTimeField()
endDate=models.DateTimeField()

type=models.CharField(max_length=50,choices=USER_TYPE_CHOICES,default=REGISTER_USER)

classDistributionRelationship(models.Model):
"""
分享
"""
#受邀人
invited_user=models.ForeignKey(User,related_name='invited_user',null=True)
#分享人
share_user=models.ForeignKey(User,related_name='share_user',null=True)

course=models.ForeignKey("course.VideoInfo",related_name="relationship_course")

createDate=models.DateTimeField(auto_now_add=True)
updateDate=models.DateTimeField(auto_now=True)

难度就在于把两张表数据糅合在一块,并且还需要一些自定义的列,导出excel

后端代码:

fromimport_exportimportresources
fromimport_exportimportfields

from.modelsimportWatchStatistics


classWatchStatisticsResource(resources.ModelResource):
	#attribute所对应的值,中间是两个下划线,链式调用
title=fields.Field(column_name='课程名称',attribute='course__title')
user=fields.Field(column_name='用户',attribute='user__owner__name')
tel=fields.Field(column_name='电话号码',attribute='user__owner__tel')
view_count=fields.Field(column_name='访问次数',attribute='view_count')
total_duration=fields.Field(column_name='累计观看时间(分钟)',attribute='total_view_time')
endDate=fields.Field(column_name='最后观看时间',attribute='endDate')
#share_user要对应到group_user.share_user
share_user=fields.Field(column_name='分享人',attribute='share_user')

classMeta:
model=WatchStatistics
fields=(
)
export_order=('title','user','tel','view_count','total_duration','endDate','share_user')
widgets={
'endDate':{'format':'%Y-%m-%d%H:%M:%S'},
}


@method_decorator(login_required,name='dispatch')
classAdminExport(View):
resource_model=None
redirect_space=None

model_class=None

datetime_fields=[]
search_fields=[]

default_filters={}

prefix=""
default_order_sort_field=['id']

defexport(self,request,queryset):
content_type=request.GET.get('content_type','application/vnd.ms-excel')
dataset=self.resource_model().export(queryset=queryset)

filename="%s_%s"%(self.prefix,utils.datetime2string())
if"excel"incontent_type:
suffix="xls"
elif"csv"incontent_type:
suffix="csv"
else:
messages.error(request,u'导出格式有误!')
logger.info("exportformaterror")
returnredirect(self.redirect_space)

content=getattr(dataset,suffix)
##content_type这里响应对象获得了一个特殊的mime类型,告诉浏览器这是个excel文件不是html
response=HttpResponse(content,content_type=content_type)
##这里响应对象获得了附加的Content-Disposition协议头,它含有excel文件的名称,文件名随意,当浏览器访问它时,会以"另存为"对话框中使用它.
response['Content-Disposition']='attachment;filename=%s'%(
'{}.{}'.format(filename,suffix),)
returnresponse

classWatchStatisticsExport(AdminExport):
resource_model=WatchStatisticsResource
redirect_space='analysis:time'

model_class=WatchStatistics

prefix="watchstatistics"
datetime_fields=[]
search_fields=[]

default_filters={}

defget(self,request):
course_id=self.request.GET.get('course_id')
start_time=self.request.GET.get('start_time')
end_time=self.request.GET.get('end_time')

watchstatistics_list=WatchStatistics.objects.filter(course=course_id).select_related().order_by('-endDate')

ifstart_time:
watchstatistics_list=watchstatistics_list.filter(endDate__gte=start_time)

ifend_time:
watchstatistics_list=watchstatistics_list.filter(endDate__lte=end_time)

#查询出当前课程的分享表
distribution_list=DistributionRelationship.objects.filter(course=course_id).select_related()
invited_user_list=[]

base_user=OrderedDict()
forwatchinwatchstatistics_list:
user=base_user.setdefault(watch.user_id,[])
user.insert(0,watch)
#将分享表里受邀人的id所在记录append
fordisindistribution_list:
#将拥有受邀人的id记录在列表,加入多条的话,说明同一直播,此人被不同的人邀请进来或者分享人多次访问会造成多条记录
ifdis.invited_user.id==watch.user_id:
user.append(dis)
invited_user_list.append(watch.user_id)

watch_records=[]
foruser_id,usersinbase_user.items():
#判断如果有分享的记录,则excel加入邀请人
ifnotinvited_user_list:
group_user=users[0]
group_user.view_count=len(users)
total_view_time=sum([u.durationforuinusers])
group_user.total_view_time=total_view_time
watch_records.append(group_user)
else:
#判断如果学员与受邀人一致,则分开处理
group_user=users[0]
ifgroup_user.user.idininvited_user_list:
#同一个人在同一直播被多人邀请过,查找次数
num=invited_user_list.count(group_user.user.id)
#减去分享记录和访问次数
group_user.view_count=len(users)-num
total_view_time=sum([u.durationforuinusers[:-num]])
group_user.total_view_time=total_view_time
share_user=users[-1].share_user.owner.name
group_user.share_user=share_user
else:
group_user.view_count=len(users)
total_view_time=sum([u.durationforuinusers])
group_user.total_view_time=total_view_time
group_user.share_user='无'
watch_records.append(group_user)

returnself.export(request,queryset=watch_records)

效果如图:

您需要 登录账户 后才能发表评论

取消回复欢迎 发表评论:

关灯