diff --git a/Day21-30/21-30.Web前端概述.md b/Day21-30/21-30.Web前端概述.md
index 0ea3daf..0e4baeb 100644
--- a/Day21-30/21-30.Web前端概述.md
+++ b/Day21-30/21-30.Web前端概述.md
@@ -1,6 +1,6 @@
## Web前端概述
-> 说明:本文使用的部分插图来自*Jon Duckett*先生的*[HTML and CSS: Design and Build Websites](https://www.amazon.cn/dp/1118008189/ref=sr_1_5?__mk_zh_CN=%E4%BA%9A%E9%A9%AC%E9%80%8A%E7%BD%91%E7%AB%99&keywords=html+%26+css&qid=1554609325&s=gateway&sr=8-5)*一书,这是一本非常棒的前端入门书,有兴趣的读者可以在亚马逊或者其他网站上找到该书的购买链接。
+> **说明**:本文使用的部分插图来自*Jon Duckett*先生的*[HTML and CSS: Design and Build Websites](https://www.amazon.cn/dp/1118008189/ref=sr_1_5?__mk_zh_CN=%E4%BA%9A%E9%A9%AC%E9%80%8A%E7%BD%91%E7%AB%99&keywords=html+%26+css&qid=1554609325&s=gateway&sr=8-5)*一书,这是一本非常棒的前端入门书,有兴趣的读者可以在亚马逊或者其他网站上找到该书的购买链接。
### HTML简史
diff --git a/Day31-35/31-35.玩转Linux操作系统.md b/Day31-35/31-35.玩转Linux操作系统.md
index 97a1040..2bcc598 100644
--- a/Day31-35/31-35.玩转Linux操作系统.md
+++ b/Day31-35/31-35.玩转Linux操作系统.md
@@ -1,6 +1,6 @@
## 玩转Linux操作系统
-> 说明:本文中对Linux命令的讲解都是基于名为CentOS的Linux发行版本,我自己使用的是阿里云服务器,系统版本为CentOS Linux release 7.6.1810。不同的Linux发行版本在Shell命令和工具程序上会有一些差别,但是这些差别是很小的。
+> **说明**:本文中对Linux命令的讲解都是基于名为CentOS的Linux发行版本,我自己使用的是阿里云服务器,系统版本为CentOS Linux release 7.6.1810。不同的Linux发行版本在Shell命令和工具程序上会有一些差别,但是这些差别是很小的。
### 操作系统发展史
diff --git a/Day36-40/39-40.NoSQL入门.md b/Day36-40/39-40.NoSQL入门.md
index 35ab2ca..0b97878 100644
--- a/Day36-40/39-40.NoSQL入门.md
+++ b/Day36-40/39-40.NoSQL入门.md
@@ -16,7 +16,7 @@ NoSQL数据库按照其存储类型可以大致分为以下几类:
| 图数据库 | Neo4J
FlockDB
JanusGraph | 使用图结构进行语义查询的数据库,它使用节点、边和属性来表示和存储数据。图数据库从设计上,就可以简单快速的检索难以在关系系统中建模的复杂层次结构。 |
| 对象数据库 | db4o
Versant | 通过类似面向对象语言的语法操作数据库,通过对象的方式存取数据。 |
-> 说明:想了解更多的NoSQL数据库,可以访问。
+> **说明**:想了解更多的NoSQL数据库,可以访问。
### Redis概述
@@ -144,7 +144,7 @@ Redis有着非常丰富的数据类型,也有很多的命令来操作这些数
![](./res/redis-data-types.png)
-> 说明:上面的插图来自付磊和张益军先生编著的《Redis开发与运维》一书。
+> **说明**:上面的插图来自付磊和张益军先生编著的《Redis开发与运维》一书。
```Shell
127.0.0.1:6379> set username admin
@@ -297,7 +297,7 @@ MongoDB是2009年问世的一个面向文档的数据库管理系统,由C++语
MongoDB将数据存储为一个文档,一个文档由一系列的“键值对”组成,其文档类似于JSON对象,但是MongoDB对JSON进行了二进制处理(能够更快的定位key和value),因此其文档的存储格式称为BSON。关于JSON和BSON的差别大家可以看看MongoDB官方网站的文章[《JSON and BSON》](https://www.mongodb.com/json-and-bson)。
-目前,MongoDB已经提供了对Windows、MacOS、Linux、Solaris等多个平台的支持,而且也提供了多种开发语言的驱动程序,Python当然是其中之一。
+目前,MongoDB已经提供了对Windows、macOS、Linux、Solaris等多个平台的支持,而且也提供了多种开发语言的驱动程序,Python当然是其中之一。
#### MongoDB的安装和配置
@@ -320,7 +320,7 @@ mongod --bind_ip 172.18.61.250
2018-06-03T18:03:28.945+0800 I NETWORK [initandlisten] waiting for connections on port 27017
```
-> 说明:上面的操作中,export命令是设置PATH环境变量,这样可以在任意路径下执行mongod来启动MongoDB服务器。MongoDB默认保存数据的路径是/data/db目录,为此要提前创建该目录。此外,在使用mongod启动MongoDB服务器时,--bind_ip参数用来将服务绑定到指定的IP地址,也可以用--port参数来指定端口,默认端口为27017。
+> **说明**:上面的操作中,export命令是设置PATH环境变量,这样可以在任意路径下执行mongod来启动MongoDB服务器。MongoDB默认保存数据的路径是/data/db目录,为此要提前创建该目录。此外,在使用mongod启动MongoDB服务器时,--bind_ip参数用来将服务绑定到指定的IP地址,也可以用--port参数来指定端口,默认端口为27017。
#### MongoDB基本概念
diff --git a/Day41-55/45.制作报表.md b/Day41-55/45.制作报表.md
index eeb0208..ecf9c6b 100644
--- a/Day41-55/45.制作报表.md
+++ b/Day41-55/45.制作报表.md
@@ -8,7 +8,15 @@
报表 = 多样的格式 + 动态的数据
```
-有很多的三方库支持在Python程序中写Excel文件,包括[xlwt]()、[xlwings]()、[openpyxl]()、[xlswriter]()、[pandas]()等,其中的xlwt虽然只支持写xls格式的Excel文件,但在性能方面的表现还是不错的。下面我们就以xlwt为例,来演示如何在Django项目中导出Excel报表,例如导出一个包含所有老师信息的Excel表格。
+有很多的三方库支持在Python程序中写Excel文件,包括[`xlwt`]()、[`xlwings`]()、[`openpyxl`]()、[`xlswriter`]()、[`pandas`]()等,其中的xlwt虽然只支持写xls格式的Excel文件,但在性能方面的表现还是不错的。下面我们就以`xlwt`为例,来演示如何在Django项目中导出Excel报表。
+
+安装`xlwt`。
+
+```Bash
+pip install xlwt
+```
+
+导出包含所有老师信息的Excel表格的视图函数。
```Python
def export_teachers_excel(request):
@@ -46,15 +54,15 @@ def export_teachers_excel(request):
```Python
urlpatterns = [
- # 此处省略上面的代码
+
path('excel/', views.export_teachers_excel),
- # 此处省略下面的代码
+
]
```
### 导出PDF报表
-在Django项目中,如果需要导出PDF报表,可以借助三方库reportlab来生成PDF文件的内容,再将文件的二进制数据输出给浏览器并指定MIME类型为`application/pdf`,具体的代码如下所示。
+在Django项目中,如果需要导出PDF报表,可以借助三方库`reportlab`来生成PDF文件的内容,再将文件的二进制数据输出给浏览器并指定MIME类型为`application/pdf`,具体的代码如下所示。
```Python
def export_pdf(request: HttpRequest) -> HttpResponse:
@@ -70,7 +78,7 @@ def export_pdf(request: HttpRequest) -> HttpResponse:
return resp
```
-关于如何用reportlab定制PDF报表的内容,可以参考reportlab的[官方文档](https://www.reportlab.com/docs/reportlab-userguide.pdf)。
+关于如何用`reportlab`定制PDF报表的内容,可以参考reportlab的[官方文档](https://www.reportlab.com/docs/reportlab-userguide.pdf)。
### 生成前端统计图表
@@ -89,7 +97,7 @@ def get_teachers_data(request):
```Python
urlpatterns = [
- path('teachers_data/', views.export_teachers_excel),
+ path('teachers_data/', views.get_teachers_data),
]
```
diff --git a/Day41-55/46.日志和调试工具栏.md b/Day41-55/46.日志和调试工具栏.md
index 182f687..a880881 100644
--- a/Day41-55/46.日志和调试工具栏.md
+++ b/Day41-55/46.日志和调试工具栏.md
@@ -169,9 +169,9 @@ LOGGING = {
4. 在配置好Django-Debug-Toolbar之后,页面右侧会看到一个调试工具栏,如下图所示,上面包括了如前所述的各种调试信息,包括执行时间、项目设置、请求头、SQL、静态资源、模板、缓存、信号等,查看起来非常的方便。
- ![](res/django-debug-toolbar.png)
+ ![](res/debug-toolbar.png)
-#### 优化ORM代码
+### 优化ORM代码
在配置了日志或Django-Debug-Toolbar之后,我们可以查看一下之前将老师数据导出成Excel报表的视图函数执行情况,这里我们关注的是ORM框架生成的SQL查询到底是什么样子的,相信这里的结果会让你感到有一些意外。执行`Teacher.objects.all()`之后我们可以注意到,在控制台看到的或者通过Django-Debug-Toolbar输出的SQL是下面这样的:
@@ -185,7 +185,7 @@ SELECT `tb_subject`.`no`, `tb_subject`.`name`, `tb_subject`.`intro`, `tb_subject
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()`方法来加载关联对象。
+这里的问题通常被称为“1+N查询”(有的地方也将其称之为“N+1查询”),原本获取老师的数据只需要一条SQL,但是由于老师关联了学科,当我们查询到`N`条老师的数据时,Django的ORM框架又向数据库发出了`N`条SQL去查询老师所属学科的信息。每条SQL执行都会有较大的开销而且会给数据库服务器带来压力,如果能够在一条SQL中完成老师和学科的查询肯定是更好的做法,这一点也很容易做到,相信大家已经想到怎么做了。是的,我们可以使用连接查询,但是在使用Django的ORM框架时如何做到这一点呢?对于多对一关联(如投票应用中的老师和学科),我们可以使用`QuerySet`的用`select_related()`方法来加载关联对象;而对于多对多关联(如电商网站中的订单和商品),我们可以使用`prefetch_related()`方法来加载关联对象。
在导出老师Excel报表的视图函数中,我们可以按照下面的方式优化代码。
@@ -202,15 +202,13 @@ queryset = Teacher.objects.all().only('name', 'good_count', 'bad_count')
当然,如果要统计出每个学科的老师好评和差评的平均数,利用Django的ORM框架也能够做到,代码如下所示:
```Python
-queryset = Teacher.objects.values('subject').annotate(
- good=Avg('good_count'), bad=Avg('bad_count'))
+queryset = Teacher.objects.values('subject').annotate(good=Avg('good_count'), bad=Avg('bad_count'))
```
这里获得的`QuerySet`中的元素是字典对象,每个字典中有三组键值对,分别是代表学科编号的`subject`、代表好评数的`good`和代表差评数的`bad`。如果想要获得学科的名称而不是编号,可以按照如下所示的方式调整代码:
```Python
-queryset = Teacher.objects.values('subject__name').annotate(
- good=Avg('good_count'), bad=Avg('bad_count'))
+queryset = Teacher.objects.values('subject__name').annotate(good=Avg('good_count'), bad=Avg('bad_count'))
```
可见,Django的ORM框架允许我们用面向对象的方式完成关系数据库中的分组和聚合查询。
diff --git a/Day41-55/47.中间件的应用.md b/Day41-55/47.中间件的应用.md
index 01470e9..1a670d0 100644
--- a/Day41-55/47.中间件的应用.md
+++ b/Day41-55/47.中间件的应用.md
@@ -51,9 +51,7 @@ from django.http import JsonResponse
from django.shortcuts import redirect
# 需要登录才能访问的资源路径
-LOGIN_REQUIRED_URLS = {
- '/praise/', '/criticize/', '/excel/', '/teachers_data/',
-}
+LOGIN_REQUIRED_URLS = {'/praise/', '/criticize/', '/excel/', '/teachers_data/'}
def check_login_middleware(get_resp):
@@ -76,7 +74,37 @@ def check_login_middleware(get_resp):
return wrapper
```
-修改配置文件,激活中间件使其生效。
+当然,我们也可以定义一个类来充当装饰器,如果类中有`__call__`魔术方法,这个类的对象就像函数一样可调用,所以下面是另一种实现中间件的方式,道理跟上面的代码完全一样。
+
+还有一种基于类实现中间件的方式,这种方式在较新版本的Django中已经不推荐使用了,但是大家接触到的代码中,仍然有可能遇到这种写法,大致的代码如下所示。
+
+```Python
+from django.utils.deprecation import MiddlewareMixin
+
+
+class MyMiddleware(MiddlewareMixin):
+
+ def process_request(self, request):
+ pass
+
+ def process_view(self, request, view_func, view_args, view_kwargs):
+ pass
+
+ def process_template_response(self, request, response):
+ pass
+
+ def process_response(self, request, response):
+ pass
+
+ def process_exception(self, request, exception):
+ pass
+```
+
+上面类中的五个方法都是中间件的钩子函数,分别在收到用户请求、进入视图函数之前、渲染模板、返回响应和出现异常的时候被回调。当然,写不写这些方法是根据中间件的需求来确定的,并不是所有的场景都需要重写五个方法,下面的图相信能够帮助大家理解这种写法。
+
+![](res/django-middleware.png)
+
+写好中间件代码后,需要修改配置文件来激活中间件使其生效。
```Python
MIDDLEWARE = [
@@ -95,3 +123,4 @@ MIDDLEWARE = [
注意上面这个中间件列表中元素的顺序,当收到来自用户的请求时,中间件按照从上到下的顺序依次执行,这行完这些中间件以后,请求才会最终到达视图函数。当然,在这个过程中,用户的请求可以被拦截,就像上面我们自定义的中间件那样,如果用户在没有登录的情况下访问了受保护的资源,中间件会将请求直接重定向到登录页,后面的中间件和视图函数将不再执行。在响应用户请求的过程中,上面的中间件会按照从下到上的顺序依次执行,这样的话我们还可以对响应做进一步的处理。
中间件执行的顺序是非常重要的,对于有依赖关系的中间件必须保证被依赖的中间件要置于依赖它的中间件的前面,就好比我们刚才自定义的中间件要放到`SessionMiddleware`的后面,因为我们要依赖这个中间件为请求绑定的`session`对象才能判定用户是否登录。
+
diff --git a/Day41-55/res/debug-toolbar.png b/Day41-55/res/debug-toolbar.png
new file mode 100644
index 0000000..5d997e4
Binary files /dev/null and b/Day41-55/res/debug-toolbar.png differ
diff --git a/Day41-55/res/django-middleware.png b/Day41-55/res/django-middleware.png
new file mode 100644
index 0000000..9e96567
Binary files /dev/null and b/Day41-55/res/django-middleware.png differ
diff --git a/res/donation5.png b/res/donation5.png
new file mode 100644
index 0000000..df0fd68
Binary files /dev/null and b/res/donation5.png differ
diff --git a/更新日志.md b/更新日志.md
index 68dbe37..d7a1b24 100644
--- a/更新日志.md
+++ b/更新日志.md
@@ -1,5 +1,15 @@
## 更新日志
+### 2020年7月12日
+
+1. 修正了部分文档中的bug。
+
+2. 更新了Django部分的文档。
+
+3. 将近期收到的打赏捐赠给陕西回归儿童救助中心。
+
+ ![](res/donation5.png)
+
### 2020年4月8日
1. 将基础部分(第1天到第15天)内容重新创建了一个名为“Python-Core-50-Courses”的仓库,更新了部分内容。