本篇內(nèi)容主要講解“怎么理解Python報表和日志”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“怎么理解Python報表和日志”吧!
10年積累的成都網(wǎng)站設(shè)計、網(wǎng)站建設(shè)經(jīng)驗(yàn),可以快速應(yīng)對客戶對網(wǎng)站的新想法和需求。提供各種問題對應(yīng)的解決方案。讓選擇我們的客戶得到更好、更有力的網(wǎng)絡(luò)服務(wù)。我雖然不認(rèn)識你,你也不認(rèn)識我。但先網(wǎng)站設(shè)計后付款的網(wǎng)站建設(shè)流程,更有船營免費(fèi)網(wǎng)站建設(shè)讓你可以放心的選擇與我們合作。
導(dǎo)出Excel報表
報表就是用表格、圖表等格式來動態(tài)顯示數(shù)據(jù),所以有人用這樣的公式來描述報表:
報表 = 多樣的格式 + 動態(tài)的數(shù)據(jù)
有很多的三方庫支持在Python程序中寫Excel文件,包括xlwt、xlwings、openpyxl、xlswriter、pandas等,其中的xlwt雖然只支持寫xls格式的Excel文件,但在性能方面的表現(xiàn)還是不錯的。下面我們就以xlwt為例,來演示如何在Django項(xiàng)目中導(dǎo)出Excel報表,例如導(dǎo)出一個包含所有老師信息的Excel表格。
def export_teachers_excel(request):
# 創(chuàng)建工作簿
wb = xlwt.Workbook()
# 添加工作表
sheet = wb.add_sheet('老師信息表')
# 查詢所有老師的信息(注意:這個地方稍后需要優(yōu)化)
queryset = Teacher.objects.all()
# 向Excel表單中寫入表頭
colnames = ('姓名',
'介紹',
'好評數(shù)',
'差評數(shù)',
'學(xué)科')
for index, name
in enumerate(colnames):
sheet.write(0, index, name)
# 向單元格中寫入老師的數(shù)據(jù)
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)
# 將二進(jìn)制數(shù)據(jù)寫入響應(yīng)的消息體中并設(shè)置MIME類型
resp = HttpResponse(buffer.getvalue(), content_type='application/vnd.ms-excel')
# 中文文件名需要處理成百分號編碼
filename = quote('老師.xls')
# 通過響應(yīng)頭告知瀏覽器下載該文件以及對應(yīng)的文件名
resp['content-disposition'] = f'attachment; filename="{filename}"'
return resp
映射URL。
urlpatterns = [
# 此處省略上面的代碼
path('excel/', views.export_teachers_excel),
# 此處省略下面的代碼
]
生成前端統(tǒng)計圖表
如果項(xiàng)目中需要生成前端統(tǒng)計圖表,可以使用百度的ECharts。具體的做法是后端通過提供數(shù)據(jù)接口返回統(tǒng)計圖表所需的數(shù)據(jù),前端使用ECharts來渲染出柱狀圖、折線圖、餅圖、散點(diǎn)圖等圖表。例如我們要生成一個統(tǒng)計所有老師好評數(shù)和差評數(shù)的報表,可以按照下面的方式來做。
def get_teachers_data(request):
# 查詢所有老師的信息(注意:這個地方稍后也需要優(yōu)化)
queryset = Teacher.objects.all()
# 用生成式將老師的名字放在一個列表中
names = [teacher.name
for teacher
in queryset]
# 用生成式將老師的好評數(shù)放在一個列表中
good = [teacher.good_count
for teacher
in queryset]
# 用生成式將老師的差評數(shù)放在一個列表中
bad = [teacher.bad_count
for teacher
in queryset]
# 返回JSON格式的數(shù)據(jù)
return JsonResponse({'names': names,
'good': good,
'bad': bad})
映射URL。
urlpatterns = [
# 此處省略上面的代碼
path('teachers_data/', views.export_teachers_excel),
# 此處省略下面的代碼
]
使用ECharts生成柱狀圖。
老師評價統(tǒng)計
大家運(yùn)行一下!
配置日志
項(xiàng)目開發(fā)階段,顯示足夠的調(diào)試信息以輔助開發(fā)人員調(diào)試代碼還是非常必要的;項(xiàng)目上線以后,將系統(tǒng)運(yùn)行時出現(xiàn)的警告、錯誤等信息記錄下來以備相關(guān)人員了解系統(tǒng)運(yùn)行狀況并維護(hù)代碼也是很有必要的。要做好這兩件事件,我們需要為Django項(xiàng)目配置日志。
Django的日志配置基本可以參照官方文檔再結(jié)合項(xiàng)目實(shí)際需求來進(jìn)行,這些內(nèi)容基本上可以從官方文檔上復(fù)制下來,然后進(jìn)行局部的調(diào)整即可,下面給出一些參考配置。
LOGGING = {
'version':
1,
# 是否禁用已經(jīng)存在的日志器
'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',
},
}
}
大家可能已經(jīng)注意到了,上面日志配置中的formatters是日志格式化器,它代表了如何格式化輸出日志,其中格式占位符分別表示:
%(name)s - 記錄器的名稱
%(levelno)s - 數(shù)字形式的日志記錄級別
%(levelname)s - 日志記錄級別的文本名稱
%(filename)s - 執(zhí)行日志記錄調(diào)用的源文件的文件名稱
%(pathname)s - 執(zhí)行日志記錄調(diào)用的源文件的路徑名稱
%(funcName)s - 執(zhí)行日志記錄調(diào)用的函數(shù)名稱
%(module)s - 執(zhí)行日志記錄調(diào)用的模塊名稱
%(lineno)s - 執(zhí)行日志記錄調(diào)用的行號
%(created)s - 執(zhí)行日志記錄的時間
%(asctime)s - 日期和時間
%(msecs)s - 毫秒部分
%(thread)d - 線程ID(整數(shù))
%(threadName)s - 線程名稱
%(process)d - 進(jìn)程ID (整數(shù))
日志配置中的handlers用來指定日志處理器,簡單的說就是指定將日志輸出到控制臺還是文件又或者是網(wǎng)絡(luò)上的服務(wù)器,可用的處理器包括:
logging.StreamHandler(stream=None) - 可以向類似與sys.stdout或者sys.stderr的任何文件對象輸出信息
logging.FileHandler(filename, mode='a', encoding=None, delay=False) - 將日志消息寫入文件
logging.handlers.DatagramHandler(host, port) - 使用UDP協(xié)議,將日志信息發(fā)送到指定主機(jī)和端口的網(wǎng)絡(luò)主機(jī)上
logging.handlers.HTTPHandler(host, url) - 使用HTTP的GET或POST方法將日志消息上傳到一臺HTTP 服務(wù)器
logging.handlers.RotatingFileHandler(filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=False) - 將日志消息寫入文件,如果文件的大小超出maxBytes指定的值,那么將重新生成一個文件來記錄日志
logging.handlers.SocketHandler(host, port) - 使用TCP協(xié)議,將日志信息發(fā)送到指定主機(jī)和端口的網(wǎng)絡(luò)主機(jī)上
logging.handlers.SMTPHandler(mailhost, fromaddr, toaddrs, subject, credentials=None, secure=None, timeout=1.0) - 將日志輸出到指定的郵件地址
logging.MemoryHandler(capacity, flushLevel=ERROR, target=None, flushOnClose=True) - 將日志輸出到內(nèi)存指定的緩沖區(qū)中
上面每個日志處理器都指定了一個名為“l(fā)evel”的屬性,它代表了日志的級別,不同的日志級別反映出日志中記錄信息的嚴(yán)重性。Python中定義了六個級別的日志,按照從低到高的順序依次是:NOTSET、DEBUG、INFO、WARNING、ERROR、CRITICAL。
最后配置的日志記錄器是用來真正輸出日志的,Django框架提供了如下所示的內(nèi)置記錄器:
django - 在Django層次結(jié)構(gòu)中的所有消息記錄器
django.request - 與請求處理相關(guān)的日志消息。5xx響應(yīng)被視為錯誤消息;4xx響應(yīng)被視為為警告消息
django.server - 與通過runserver調(diào)用的服務(wù)器所接收的請求相關(guān)的日志消息。5xx響應(yīng)被視為錯誤消息;4xx響應(yīng)被記錄為警告消息;其他一切都被記錄為INFO
django.template - 與模板渲染相關(guān)的日志消息
django.db.backends - 有與數(shù)據(jù)庫交互產(chǎn)生的日志消息,如果希望顯示ORM框架執(zhí)行的SQL語句,就可以使用該日志記錄器。
日志記錄器中配置的日志級別有可能不是最終的日志級別,因?yàn)檫€要參考日志處理器中配置的日志級別,取二者中級別較高者作為最終的日志級別。
Django-Debug-Toolbar是項(xiàng)目開發(fā)階段輔助調(diào)試和優(yōu)化的神器,只要配置了它,就可以很方便的查看到如下表所示的項(xiàng)目運(yùn)行信息,這些信息對調(diào)試項(xiàng)目和優(yōu)化Web應(yīng)用性能都是至關(guān)重要的。
安裝Django-Debug-Toolbar。pip install django-debug-toolbar
配置 - 修改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, }
配置 - 修改urls.py。if settings.DEBUG: import debug_toolbar urlpatterns.insert(0, path('__debug__/', include(debug_toolbar.urls)))
使用 - 如下圖所示,在配置好Django-Debug-Toolbar之后,頁面右側(cè)會看到一個調(diào)試工具欄,上面包括了如前所述的各種調(diào)試信息,包括執(zhí)行時間、項(xiàng)目設(shè)置、請求頭、SQL、靜態(tài)資源、模板、緩存、信號等,查看起來非常的方便。
優(yōu)化ORM代碼
在配置了日志或Django-Debug-Toolbar之后,我們可以查看一下之前將老師數(shù)據(jù)導(dǎo)出成Excel報表的視圖函數(shù)執(zhí)行情況,這里我們關(guān)注的是ORM框架生成的SQL查詢到底是什么樣子的,相信這里的結(jié)果會讓你感到有一些意外。執(zhí)行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查詢”),原本獲取老師的數(shù)據(jù)只需要一條SQL,但是由于老師關(guān)聯(lián)了學(xué)科,當(dāng)我們查詢到N條老師的數(shù)據(jù)時,Django的ORM框架又向數(shù)據(jù)庫發(fā)出了N條SQL去查詢老師所屬學(xué)科的信息。每條SQL執(zhí)行都會有較大的開銷而且會給數(shù)據(jù)庫服務(wù)器帶來壓力,如果能夠在一條SQL中完成老師和學(xué)科的查詢肯定是更好的做法,這一點(diǎn)也很容易做到,相信大家已經(jīng)想到怎么做了。是的,我們可以使用連接查詢,但是在使用Django的ORM框架時如何做到這一點(diǎn)呢?對于多對一關(guān)聯(lián)(如投票應(yīng)用中的老師和學(xué)科),我們可以使用QuerySet的用select_related()方法來加載關(guān)聯(lián)對象;而對于多對多關(guān)聯(lián)(如電商網(wǎng)站中的訂單和商品),我們可以使用prefetch_related()方法來加載關(guān)聯(lián)對象。
在導(dǎo)出老師Excel報表的視圖函數(shù)中,我們可以按照下面的方式優(yōu)化代碼。
queryset = Teacher.objects.all().select_related('subject')
事實(shí)上,用ECharts生成前端報表的視圖函數(shù)中,查詢老師好評和差評數(shù)據(jù)的操作也能夠優(yōu)化,因?yàn)樵谶@個例子中,我們只需要獲取老師的姓名、好評數(shù)和差評數(shù)這三項(xiàng)數(shù)據(jù),但是在默認(rèn)的情況生成的SQL會查詢老師表的所有字段。可以用QuerySet的only()方法來指定需要查詢的屬性,也可以用QuerySet的defer()方法來指定暫時不需要查詢的屬性,這樣生成的SQL會通過投影操作來指定需要查詢的列,從而改善查詢性能,代碼如下所示:
queryset = Teacher.objects.all().only('name', 'good_count', 'bad_count')
當(dāng)然,如果要統(tǒng)計出每個學(xué)科的老師好評和差評的平均數(shù),利用Django的ORM框架也能夠做到,代碼如下所示:
queryset = Teacher.objects.values('subject').annotate(
good=Avg('good_count'), bad=Avg('bad_count'))
這里獲得的QuerySet中的元素是字典對象,每個字典中有三組鍵值對,分別是代表學(xué)科編號的subject、代表好評數(shù)的good和代表差評數(shù)的bad。如果想要獲得學(xué)科的名稱而不是編號,可以按照如下所示的方式調(diào)整代碼:
queryset = Teacher.objects.values('subject__name').annotate(
good=Avg('good_count'), bad=Avg('bad_count'))
可見,Django的ORM框架允許我們用面向?qū)ο蟮姆绞酵瓿申P(guān)系數(shù)據(jù)庫中的分組和聚合查詢。
到此,相信大家對“怎么理解Python報表和日志”有了更深的了解,不妨來實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!