程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

[Python full stack 100 day learning notes] day46 import and export excel reports and configuration logs

編輯:Python

Reports and logs

export Excel report form

A report is a form 、 Charts and other formats to dynamically display data , So some people use this formula to describe the report :

 report form = Various formats + Dynamic data

There are many third-party libraries supported in Python Write... In the program Excel file , Include xlwt、xlwings、openpyxl、xlswriter、pandas etc. , Among them xlwt Although it only supports writing xls Format Excel file , But the performance is good . So let's do that xlwt For example , To demonstrate how to Django Export from project Excel report form , For example, export a that contains all the teacher information Excel form .

def export_teachers_excel(request):
# Create Workbook 
wb = xlwt.Workbook()
# Add sheet 
sheet = wb.add_sheet(' Teacher information sheet ')
# Query the information of all teachers ( Be careful : This place needs to be optimized later )
queryset = Teacher.objects.all()
# towards Excel Write the header in the form 
colnames = (' full name ', ' Introduce ', ' Good reviews ', ' Bad reviews ', ' Discipline ')
for index, name in enumerate(colnames):
sheet.write(0, index, name)
# Write the teacher's data to the cell 
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)
# preservation Excel
buffer = BytesIO()
wb.save(buffer)
# Write binary data into the message body of the response and set MIME type 
resp = HttpResponse(buffer.getvalue(), content_type='application/vnd.ms-excel')
# The Chinese file name needs to be encoded with a percent sign 
filename = quote(' teacher .xls')
# Tell the browser to download the file and the corresponding file name through the response header 
resp['content-disposition'] = f'attachment; filename="{
filename}"'
return resp

mapping URL.

urlpatterns = [
# The above code is omitted here 
path('excel/', views.export_teachers_excel),
# The following code is omitted here 
]

Generate front-end statistical charts

If the front-end statistical chart needs to be generated in the project , Can use Baidu's ECharts. The specific method is that the back-end returns the data required by the statistical chart by providing a data interface , Front end use ECharts To render the histogram 、 Broken line diagram 、 The pie chart 、 Scatter chart and other charts . For example, we need to generate a report that counts the positive and negative comments of all teachers , You can do it in the following way .

def get_teachers_data(request):
# Query the information of all teachers ( Be careful : This place also needs to be optimized later )
queryset = Teacher.objects.all()
# Put the teacher's name in a list with generative expression 
names = [teacher.name for teacher in queryset]
# Use the generative formula to put the teacher's praise number in a list 
good = [teacher.good_count for teacher in queryset]
# Use the generative formula to put the teacher's bad comments in a list 
bad = [teacher.bad_count for teacher in queryset]
# return JSON Formatted data 
return JsonResponse({
'names': names, 'good': good, 'bad': bad})

mapping URL.

urlpatterns = [
# The above code is omitted here 
path('teachers_data/', views.export_teachers_excel),
# The following code is omitted here 
]

Use ECharts Generate a histogram .

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title> Teacher evaluation statistics </title>
</head>
<body>
<div id="main" ></div>
<p>
<a href="/"> Back to the home page </a>
</p>
<script src="https://cdn.bootcss.com/echarts/4.2.1-rc1/echarts.min.js"></script>
<script> var myChart = echarts.init(document.querySelector('#main')) fetch('/teachers_data/') .then(resp => resp.json()) .then(json => {
 var option = {
 color: ['#f00', '#00f'], title: {
 text: ' Teacher evaluation statistical chart ' }, tooltip: {
}, legend: {
 data:[' Praise ', ' Bad review '] }, xAxis: {
 data: json.names }, yAxis: {
}, series: [ {
 name: ' Praise ', type: 'bar', data: json.good }, {
 name: ' Bad review ', type: 'bar', data: json.bad } ] } myChart.setOption(option) }) </script>
</body>
</html>

The operation effect is shown in the figure below .

Configuration log

Project development stage , It is necessary to display enough debugging information to assist developers in debugging code ; After the project goes online , The warning that appears when the system is running 、 It is also necessary to record errors and other information for relevant personnel to understand the system operation and maintain the code . Do these two things well , We need to Django Project configuration log .

Django The log configuration of can be basically carried out by referring to the official documents and combining the actual needs of the project , These contents can basically be copied from official documents , Then make local adjustments , Here are some reference configurations .

LOGGING = {

'version': 1,
# Whether to disable the existing logger 
'disable_existing_loggers': False,
# Log formatter 
'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',
}
},
# Log filter 
'filters': {

# Only in Django In profile DEBUG The value is True It works when 
'require_debug_true': {

'()': 'django.utils.log.RequireDebugTrue',
},
},
# Log processor 
'handlers': {

# Output to console 
'console': {

'class': 'logging.StreamHandler',
'level': 'DEBUG',
'filters': ['require_debug_true'],
'formatter': 'simple',
},
# output to a file ( Cut once a week )
'file1': {

'class': 'logging.handlers.TimedRotatingFileHandler',
'filename': 'access.log',
'when': 'W0',
'backupCount': 12,
'formatter': 'simple',
'level': 'INFO',
},
# output to a file ( Cut once a day )
'file2': {

'class': 'logging.handlers.TimedRotatingFileHandler',
'filename': 'error.log',
'when': 'D',
'backupCount': 31,
'formatter': 'verbose',
'level': 'WARNING',
},
},
# Logger logger 
'loggers': {

'django': {

# Log processor needed 
'handlers': ['console', 'file1', 'file2'],
# Whether to propagate log information upward 
'propagate': True,
# The level of logging ( Not necessarily the final log level )
'level': 'DEBUG',
},
}
}

You may have noticed , In the log configuration above formatters yes Log formatter , It represents how to format the output log , The format placeholders represent :

  1. %(name)s - The name of the recorder
  2. %(levelno)s - Logging level in digital form
  3. %(levelname)s - Text name of logging level
  4. %(filename)s - The file name of the source file that performs the logging call
  5. %(pathname)s - Path name of the source file where the logging call is performed
  6. %(funcName)s - The name of the function that performs the logging call
  7. %(module)s - The name of the module that performs the logging call
  8. %(lineno)s - The line number of the execution logging call
  9. %(created)s - The time when logging was performed
  10. %(asctime)s - Date and time
  11. %(msecs)s - The millisecond part
  12. %(thread)d - Threads ID( Integers )
  13. %(threadName)s - Thread name
  14. %(process)d - process ID ( Integers )

In log configuration handlers Used to specify Log processor , Simply put, it specifies whether the log is output to the console, a file, or a server on the network , Available processors include :

  1. logging.StreamHandler(stream=None) - Can be similar to sys.stdout perhaps sys.stderr Output information of any file object
  2. logging.FileHandler(filename, mode=‘a’, encoding=None, delay=False) - Write log messages to file
  3. logging.handlers.DatagramHandler(host, port) - Use UDP agreement , Send log information to the network host of the specified host and port
  4. logging.handlers.HTTPHandler(host, url) - Use HTTP Of GET or POST Method to upload log messages to a HTTP The server
  5. logging.handlers.RotatingFileHandler(filename, mode=‘a’, maxBytes=0, backupCount=0, encoding=None, delay=False) - Write log messages to file , If the file size exceeds maxBytes The specified value , Then a file will be regenerated to record the log
  6. logging.handlers.SocketHandler(host, port) - Use TCP agreement , Send log information to the network host of the specified host and port
  7. logging.handlers.SMTPHandler(mailhost, fromaddr, toaddrs, subject, credentials=None, secure=None, timeout=1.0) - Output the log to the specified email address
  8. logging.MemoryHandler(capacity, flushLevel=ERROR, target=None, flushOnClose=True) - Output the log to the buffer specified in memory

Each of the above log processors is assigned a name “level” Properties of , It represents the level of logs , Different log levels reflect the severity of the information recorded in the log .Python Six levels of logs are defined in , The order from low to high is :NOTSET、DEBUG、INFO、WARNING、ERROR、CRITICAL.

Last configured Loggers It is used to really output logs ,Django The framework provides a built-in recorder as shown below :

  1. django - stay Django All message loggers in the hierarchy
  2. django.request - Log messages related to request processing .5xx The response is treated as an error message ;4xx The response is treated as a warning message
  3. django.server - And through runserver Log messages related to requests received by the calling server .5xx The response is treated as an error message ;4xx The response is recorded as a warning message ; Everything else is recorded as INFO
  4. django.template - Log messages related to template rendering
  5. django.db.backends - There are log messages generated by interaction with the database , If you want to display ORM Framework implementation SQL sentence , You can use the logger .

The log level configured in the logger may not be the final log level , Because you also need to refer to the log level configured in the log processor , Take the higher level of the two as the final log level .

To configure Django-Debug-Toolbar

Django-Debug-Toolbar It is an artifact to assist debugging and Optimization in the project development stage , As long as it is configured , You can easily view the project operation information shown in the following table , This information is important for debugging projects and optimization Web Application performance is crucial .

project explain VersionsDjango Version of Time Time spent displaying views Settings The value set in the configuration file HeadersHTTP Information of request header and response header Request Various variables related to the request and their information StaticFiles Static file loading Templates Information about the template Cache Cache usage SignalsDjango Built in signal information Logging Recorded log information SQL Sent to the database SQL Statement and its execution time
  1. install Django-Debug-Toolbar.

    pip install django-debug-toolbar
    
  2. To configure - modify settings.py.

    INSTALLED_APPS = [
    'debug_toolbar',
    ]
    MIDDLEWARE = [
    'debug_toolbar.middleware.DebugToolbarMiddleware',
    ]
    DEBUG_TOOLBAR_CONFIG = {
    
    # introduce jQuery library 
    'JQUERY_URL': 'https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js',
    # Whether the toolbar is collapsed 
    'SHOW_COLLAPSED': True,
    # Show toolbar or not 
    'SHOW_TOOLBAR_CALLBACK': lambda x: True,
    }
    
  3. To configure - modify urls.py.

    if settings.DEBUG:
    import debug_toolbar
    urlpatterns.insert(0, path('__debug__/', include(debug_toolbar.urls)))
    
  4. Use - As shown in the figure below , The configured Django-Debug-Toolbar after , A debugging toolbar will be seen on the right side of the page , The above includes various debugging information as described above , Including execution time 、 Project settings 、 Request header 、SQL、 Static resources 、 Templates 、 cache 、 Signals, etc , It's very convenient to check .

Optimize ORM Code

After configuring logs or Django-Debug-Toolbar after , We can export the teacher data to Excel The execution of the view function of the report , Here we focus on ORM Frame generated SQL What exactly does the query look like , I believe the results here will make you feel a little surprised . perform Teacher.objects.all() Then we can notice , Seen on the console or through Django-Debug-Toolbar Output SQL It looks like this :

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,)

The problem here is often called “1+N Inquire about ”( or “N+1 Inquire about ”), Originally, only one piece of data is needed to obtain the teacher's data SQL, But because the teacher is related to the subject , When we find N The teacher's data ,Django Of ORM The framework sends a message to the database N strip SQL Go to inquire the information of the subject of the teacher . Every one of them SQL Execution will have a large overhead and will bring pressure to the database server , If you can be in one SQL It is certainly a better way to complete the inquiry of teachers and subjects , This is also easy to do , I believe you have figured out how to do it . Yes , We can use connection query , But in use Django Of ORM How to do this in the framework ? For many to one associations ( Such as teachers and subjects in voting applications ), We can use QuerySet The use of select_related() Method to load the associated object ; For many to many Association ( Such as orders and goods in e-commerce websites ), We can use prefetch_related() Method to load the associated object .

Exporting teachers Excel In the view function of the report , We can optimize the code in the following way .

queryset = Teacher.objects.all().select_related('subject')

in fact , use ECharts In the view function that generates the front-end report , The operation of querying the data of teachers' positive and negative comments can also be optimized , Because in this case , We just need to get the teacher's name 、 The three data of positive and negative comments , But it is generated by default SQL Will query all fields of the teacher table . It can be used QuerySet Of only() Method to specify the attribute to be queried , It can also be used. QuerySet Of defer() Method to specify properties that do not need to be queried for the time being , Generated in this way SQL The column to be queried will be specified through projection operation , So as to improve query performance , The code is as follows :

queryset = Teacher.objects.all().only('name', 'good_count', 'bad_count')

Of course , If you want to count the average number of teachers' positive and negative comments in each discipline , utilize Django Of ORM The framework can also do , The code is as follows :

queryset = Teacher.objects.values('subject').annotate(
good=Avg('good_count'), bad=Avg('bad_count'))

What we got here QuerySet The elements in are dictionary objects , There are three sets of key value pairs in each dictionary , They represent discipline numbers subject、 On behalf of the number of positive comments good And those representing negative ratings bad. If you want to get the name of the subject instead of the number , You can adjust the code in the following way :

queryset = Teacher.objects.values('subject__name').annotate(
good=Avg('good_count'), bad=Avg('bad_count'))

so ,Django Of ORM The framework allows us to complete grouping and aggregation queries in relational databases in an object-oriented manner .


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved