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
]
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 .

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 :
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 :
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 :
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 .
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 .
install Django-Debug-Toolbar.
pip install django-debug-toolbar
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,
}
To configure - modify urls.py.
if settings.DEBUG:
import debug_toolbar
urlpatterns.insert(0, path('__debug__/', include(debug_toolbar.urls)))
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 .
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 .