
原标题:Python学习教程:报表和日志
报表便是用表格、图表等格局来动态显现数据,所以有人用这样的公式来描绘报表:
报表 = 多样的格局 + 动态的数据
有许多的三方库支撑在Python程序中写Excel文件,包含xlwt、xlwings、openpyxl、xlswriter、pandas等,其间的xlwt尽管只支撑写xls格局的Excel文件,但在功能方面的体现仍是不错的。下面咱们就以xlwt为例,来演示如安在Django项目中导出Excel报表,例如导出一个包含全部教师信息的Excel表格。
def export_teachers_excel(request):
# 创立作业簿
wb = xlwt.Workbook()
# 增加作业表
sheet = wb.add_sheet('教师信息表')
# 查询全部教师的信息(留意:这个当地稍后需求优化)
queryset = Teacher.objects.all()
# 向Excel表单中写入表头
colnames = ('名字', '介绍', '好评数', '差评数', '学科')
for index, name in enumerate(colnames):
sheet.write(0, index, name)
# 向单元格中写入教师的数据
props = ('name', 'detail', 'good_count', 'bad_count', 'subject')
for row, teacher in enumerate(queryset):
for col, prop in enumerate(props):
value = getattr(teacher, prop, '')
if isinstance(value, Subject):
value = value.name
sheet.write(row + 1, col, value)
# 保存Excel
buffer = BytesIO()
wb.save(buffer)
# 将二进制数据写入呼应的音讯体中并设置MIME类型
resp = HttpResponse(buffer.getvalue(), content_type='application/vnd.ms-excel')
# 中文文件名需求处理成百分号编码
filename = quote('教师.xls')
# 经过呼应头奉告浏览器下载该文件以及对应的文件名
resp['content-disposition'] = f'attachment; filename="{filename}"'
return resp
映射URL。
urlpatterns = [
# 此处省掉上面的代码
path('excel/', views.export_teachers_excel),
# 此处省掉下面的代码
]
假如项目中需求生成前端计算图表,能够运用百度的ECharts。详细的做法是后端经过供给数据接口回来计算图表所需的数据,前端运用ECharts来烘托出柱状图、折线图、饼图、散点图等图表。例如咱们要生成一个计算全部教师好评数和差评数的报表,能够依照下面的办法来做。
def get_teachers_data(request):
# 查询全部教师的信息(留意:这个当地稍后也需求优化)
queryset = Teacher.objects.all()
# 用生成式将教师的名字放在一个列表中
names = [teacher.name for teacher in queryset]
# 用生成式将教师的好评数放在一个列表中
good = [teacher.good_count for teacher in queryset]
# 用生成式将教师的差评数放在一个列表中
bad = [teacher.bad_count for teacher in queryset]
# 回来JSON格局的数据
return JsonResponse({'names': names, 'good': good, 'bad': bad})
映射URL。
urlpatterns = [
# 此处省掉上面的代码
path('teachers_data/', views.export_teachers_excel),
# 此处省掉下面的代码
]
运用ECharts生成柱状图。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>教师点评计算</title>
</head>
<body>
<div id="main"></div>
<p>
<a href="/">回来主页</a>
</p>
< src="https://www.d3j.cc/file/upload/201908/29/2120301904821.jpg"></>
<>
var myChart = echarts.init(document.querySelector('#main'))
fetch('/teachers_data/')
.then(resp => resp.json())
.then(json => {
var option = {
color: ['#f00', '#00f'],
title: {
text: '教师点评计算图'
},
tooltip: {},
legend: {
data:['好评', '差评']
},
xAxis: {
data: json.names
},
yAxis: {},
series: [
{
name: '好评',
type: 'bar',
data: json.good
},
{
name: '差评',
type: 'bar',
data: json.bad
}
]
}
myChart.setOption(option)
})
</>
</body>
</html>
运转作用如下图所示。
项目开发阶段,显现满足的调试信息以辅佐开发人员调试代码仍是十分必要的;项目上线今后,将体系运转时呈现的正告、过错等信息记载下来以备相关人员了解体系运转状况并保护代码也是很有必要的。要做好这两件事情,咱们需求为Django项目装备日志。
Django的日志装备根本能够参照官方文档再结合项目实践需求来进行,这些内容根本上能够从官方文档上仿制下来,然后进行部分的调整即可,下面给出一些参阅装备。
LOGGING = {
'version': 1,
# 是否禁用现已存在的日志器
'disable_existing_loggers': False,
# 日志格局化器
'formatters': {
'simple': {
'format': '%(asctime)s %(module)s.%(funcName)s: %(message)s',
'datefmt': '%Y-%m-%d %H:%M:%S',
},
'verbose': {
'format': '%(asctime)s %(levelname)s [%(process)d-%(threadName)s] '
'%(module)s.%(funcName)s line %(lineno)d: %(message)s',
'datefmt': '%Y-%m-%d %H:%M:%S',
}
},
# 日志过滤器
'filters': {
# 只需在Django装备文件中DEBUG值为True时才起作用
'require_debug_true': {
'()': 'django.utils.log.RequireDebugTrue',
},
},
# 日志处理器
'handlers': {
# 输出到控制台
'console': {
'class': 'logging.StreamHandler',
'level': 'DEBUG',
'filters': ['require_debug_true'],
'formatter': 'simple',
},
# 输出到文件(每周切开一次)
'file1': {
'class': 'logging.handlers.TimedRotatingFileHandler',
'filename': 'access.log',
'when': 'W0',
'backupCount': 12,
'formatter': 'simple',
'level': 'INFO',
},
# 输出到文件(每天切开一次)
'file2': {
'class': 'logging.handlers.TimedRotatingFileHandler',
'filename': 'error.log',
'when': 'D',
'backupCount': 31,
'formatter': 'verbose',
'level': 'WARNING',
},
},
# 日志器记载器
'loggers': {
'django': {
# 需求运用的日志处理器
'handlers': ['console', 'file1', 'file2'],
# 是否向上传达日志信息
'propagate': True,
# 日志等级(纷歧定是终究的日志等级)
'level': 'DEBUG',
},
}
}
咱们或许现已留意到了,上面日志装备中的formatters是日志格局化器,它代表了怎样格局化输出日志,其间格局占位符别离表明:
- %(name)s - 记载器的称号
- %(levelno)s - 数字方法的日志记载等级
- %(levelname)s - 日志记载等级的文本称号
- %(filename)s - 履行日志记载调用的源文件的文件称号
- %(pathname)s - 履行日志记载调用的源文件的途径称号
- %(funcName)s - 履行日志记载调用的函数称号
- %(module)s - 履行日志记载调用的模块称号
- %(lineno)s - 履行日志记载调用的行号
- %(created)s - 履行日志记载的时刻
- %(asctime)s - 日期和时刻
- %(msecs)s - 毫秒部分
- %(thread)d - 线程ID(整数)
- %(threadName)s - 线程称号
- %(process)d - 进程ID (整数)
日志装备中的handlers用来指定日志处理器,简略的说便是指定将日志输出到控制台仍是文件又或许是网络上的服务器,可用的处理器包含:
- logging.StreamHandler(stream=None) - 能够向相似与sys.stdout或许sys.stderr的任何文件目标输出信息
- logging.FileHandler(filename, mode='a', encoding=None, delay=False) - 将日志音讯写入文件
- logging.handlers.DatagramHandler(host, port) - 运用UDP协议,将日志信息发送到指定主机和端口的网络主机上
- logging.handlers.HTTPHandler(host, url) - 运用HTTP的GET或POST办法将日志音讯上传到一台HTTP 服务器
- logging.handlers.RotatingFileHandler(filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=False) - 将日志音讯写入文件,假如文件的巨细超出maxBytes指定的值,那么将从头生成一个文件来记载日志
- logging.handlers.SocketHandler(host, port) - 运用TCP协议,将日志信息发送到指定主机和端口的网络主机上
- logging.handlers.SMTPHandler(mailhost, fromaddr, toaddrs, subject, credentials=None, secure=None, timeout=1.0) - 将日志输出到指定的邮件地址
- logging.MemoryHandler(capacity, flushLevel=ERROR, target=None, flushonClose=True) - 将日志输出到内存指定的缓冲区中
上面每个日志处理器都指定了一个名为“level”的特点,它代表了日志的等级,不同的日志等级反映出日志中记载信息的严重性。Python中界说了六个等级的日志,依照从低到高的次序依次是:NOTSET、DEBUG、INFO、WARNING、ERROR、CRITICAL。
终究装备的日志记载器是用来真实输出日志的,Django结构供给了如下所示的内置记载器:
- django - 在Django层次结构中的全部音讯记载器
- django.request - 与恳求处理相关的日志音讯。5xx呼应被视为过错音讯;4xx呼应被视为为正告音讯
- django.server - 与经过runserver调用的服务器所接纳的恳求相关的日志音讯。5xx呼应被视为过错音讯;4xx呼应被记载为正告音讯;其他全部都被记载为INFO
- django.template - 与模板烘托相关的日志音讯
- django.db.backends - 有与数据库交互发生的日志音讯,假如期望显现ORM结构履行的SQL句子,就能够运用该日志记载器。
日志记载器中装备的日志等级有或许不是终究的日志等级,由于还要参阅日志处理器中装备的日志等级,取二者中等级较高者作为终究的日志等级。
Django-Debug-Toolbar是项目开发阶段辅佐调试和优化的神器,只需装备了它,就能够很便利的检查到如下表所示的项目运转信息,这些信息对调试项目和优化Web运用功能都是至关重要的。
1 . 装置Django-Debug-Toolbar。
pip install django-debug-toolbar
2 . 装备 - 修正settings.py。
INSTALLED_APPS = [
'debug_toolbar',
]
MIDDLEWARE = [
'debug_toolbar.middleware.DebugToolbarMiddleware',
]
DEBUG_TOOLBAR_ConFIG = {
# 引进jQuery库
'JQUERY_URL': 'https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js',
# 工具栏是否折叠
'SHOW_COLLAPSED': True,
# 是否显现工具栏
'SHOW_TOOLBAR_CALLBACK': lambda x: True,
}
3 . 装备 - 修正urls.py。
if settings.DEBUG:
import debug_toolbar
urlpatterns.insert(0, path('__debug__/', include(debug_toolbar.urls)))
4 . 运用 - 如下图所示,在装备好Django-Debug-Toolbar之后,页面右侧会看到一个调试工具栏,上面包含了如前所述的各种调试信息,包含履行时刻、项目设置、恳求头、SQL、静态资源、模板、缓存、信号等,检查起来十分的便利。
在装备了日志或Django-Debug-Toolbar之后,咱们能够检查一下之前将教师数据导出成Excel报表的视图函数履行状况,这儿咱们重视的是ORM结构生成的SQL查询究竟是什么姿态的,信任这儿的成果会让你感到有一些意外。履行Teacher.objects.all()之后咱们能够留意到,在控制台看到的或许经过Django-Debug-Toolbar输出的SQL是下面这样的:
SELECt `tb_teacher`.`no`, `tb_teacher`.`name`, `tb_teacher`.`detail`, `tb_teacher`.`photo`, `tb_teacher`.`good_count`, `tb_teacher`.`bad_count`, `tb_teacher`.`sno` FROM `tb_teacher`; args=()
SELECt `tb_subject`.`no`, `tb_subject`.`name`, `tb_subject`.`intro`, `tb_subject`.`create_date`, `tb_subject`.`is_hot` FROM `tb_subject` WHERe `tb_subject`.`no` = 101; args=(101,)
SELECt `tb_subject`.`no`, `tb_subject`.`name`, `tb_subject`.`intro`, `tb_subject`.`create_date`, `tb_subject`.`is_hot` FROM `tb_subject` WHERe `tb_subject`.`no` = 101; args=(101,)
SELECt `tb_subject`.`no`, `tb_subject`.`name`, `tb_subject`.`intro`, `tb_subject`.`create_date`, `tb_subject`.`is_hot` FROM `tb_subject` WHERe `tb_subject`.`no` = 101; args=(101,)
SELECt `tb_subject`.`no`, `tb_subject`.`name`, `tb_subject`.`intro`, `tb_subject`.`create_date`, `tb_subject`.`is_hot` FROM `tb_subject` WHERe `tb_subject`.`no` = 101; args=(101,)
SELECt `tb_subject`.`no`, `tb_subject`.`name`, `tb_subject`.`intro`, `tb_subject`.`create_date`, `tb_subject`.`is_hot` FROM `tb_subject` WHERe `tb_subject`.`no` = 103; args=(103,)
SELECt `tb_subject`.`no`, `tb_subject`.`name`, `tb_subject`.`intro`, `tb_subject`.`create_date`, `tb_subject`.`is_hot` FROM `tb_subject` WHERe `tb_subject`.`no` = 103; args=(103,)
这儿的问题一般被称为“1+N查询”(或“N+1查询”),本来获取教师的数据只需求一条SQL,可是由于教师相关了学科,当咱们查询到N条教师的数据时,Django的ORM结构又向数据库发出了N条SQL去查询教师所属学科的信息。每条SQL履行都会有较大的开支并且会给数据库服务器带来压力,假如能够在一条SQL中完结教师和学科的查询肯定是更好的做法,这一点也很简单做到,信任咱们现已想到怎样做了。是的,咱们能够运用衔接查询,可是在运用Django的ORM结构时怎样做到这一点呢?关于多对一相关(如投票运用中的教师和学科),咱们能够运用QuerySet的用select_related()办法来加载相关目标;而关于多对多相关(如电商网站中的订单和产品),咱们能够运用prefetch_related()办法来加载相关目标。
在导出教师Excel报表的视图函数中,咱们能够依照下面的办法优化代码。
queryset = Teacher.objects.all().select_related('subject')
事实上,用ECharts生成前端报表的视图函数中,查询教师好评和差评数据的操作也能够优化,由于在这个比如中,咱们只需求获取教师的名字、好评数和差评数这三项数据,可是在默许的状况生成的SQL会查询教师表的全部字段。能够用QuerySet的only()办法来指定需求查询的特点,也能够用QuerySet的defer()办法来指定暂时不需求查询的特点,这样生成的SQL会经过投影操作来指定需求查询的列,然后改进查询功能,代码如下所示:
queryset = Teacher.objects.all().only('name', 'good_count', 'bad_count')
当然,假如要计算出每个学科的教师好评和差评的平均数,使用Django的ORM结构也能够做到,代码如下所示:
queryset = Teacher.objects.values('subject').annotate(
good=Avg('good_count'), bad=Avg('bad_count'))
这儿取得的QuerySet中的元素是字典目标,每个字典中有三组键值对,别离是代表学科编号的subject、代表好评数的good和代表差评数的bad。假如想要取得学科的称号而不是编号,能够依照如下所示的办法调整代码:
queryset = Teacher.objects.values('subject__name').annotate(
good=Avg('good_count'), bad=Avg('bad_count'))
可见,Django的ORM结构答应咱们用面向目标的办法完结联系数据库中的分组和聚合查询。
更多的Python学习教程也会持续为咱们更新!同伴们有什么疑问也能够留言!
责任编辑:
