Flask Web开发实战 勘误

抱歉为你带来不便!如果你发现了书中的错误,可以创建Issue反馈,或是直接联系我,谢谢!

第 1 次印刷版本勘误表

位置 错误 正确 备注/时间
1.2.2.2 P13 代码清单1-3下第2段第2行 http://helloflask.com/hello/Grey http://helloflask.com/greet/Grey 笔误
1.7 P23 第2个代码块下第1行 url_for('say_hello', name='Jack') url_for('greet', name='Jack') 笔误
2.2 P30 请求报文示例表格 URL /hello 改为 http://helloflask.com/hello?name=Grey 19.2.2
2.2 P30 请求报文示例表格 去掉主体一栏的内容 19.2.2
2.2 P30 请求报文示例表格下方正文 如果 URL 中包含查询字符串,或是提交了表单,那么报文主体将会是查询字符串和表单数据。 如果提交了表单,那么报文主体将会是表单数据(查询字符串通常会直接通过 URL 传递)。 19.2.2
2.2 P31 命令行输出 /hello /hello?name=Grey 19.2.2
2.2.1 P31 表2-3左侧下面的两行 POST对应的说明列原文为“传输数据”,修改为“创建或更新资源”;PUT对应的说明列原文为“传输文件”,修改为“创建或替换资源” 改进 18.11.18
2.2.3.3 P36 表2-6后第1个代码块 'goback/<int:year>' '/goback/<int:year>' 笔误。 18.9.28
2.3.1 P40 第3个代码块 {'Location', 'http://www.example.com'} {'Location': 'http://www.example.com'} 笔误。返回值中字典里的符号出错
2.3.2 P44 HTML小节的最后1行 HTTP HTML 笔误
2.3.2.4 P46 倒数第3个代码块 return jsonify({name: 'Grey Li', gender: 'male'}) return jsonify({'name': 'Grey Li', 'gender': 'male'}) 笔误。字典键加引号 18.10.18
2.2.3 P48 图2-11 图中的响应状态码应该为 302 Found 文图不对应 18.10.18
2.3.4.2 P50 代码清单2-5下第1段第3行 logged-in cookie logged_in cookie 排版错误 18.11.8
2.3.4.2 P51 图2-12后第1个代码块 第9行及以下均应向左缩进4个空格 排版错误 18.10.18
2.3.4.2 P52 图2-13后第1段第2行 logged-in logged_in 排版错误 18.11.8
2.3.4.2 P52 代码清单2-6下第2段第2行 logged-in logged_in 排版错误 18.11.8
2.5.4.1.(2) P67 “攻击示例”小节第3段文字第2行 设为 “';drop table users; --” 设为 “';drop table students; --” 笔误 18.11.2
4.2.3 P110 代码清单4-2最后1行 'login.html' 'basic.html' 笔误 18.9.28
4.3.1 P112 第1个代码块后第1行 3000 2000 笔误,不同的浏览器对于URL有不同的长度限制,此处长度为“最佳实践”
4.3.1 P113 代码清单4-5第1行 '/' '/basic' 笔误 18.10.18
4.3.1 P115 第 2 小节的代码块第 5 行 Length(8, 128) Length(6, 128) 前后不一致(1-3 重印时需要反过来调整另外 3 处),18.12.28
4.4.4.3.(3)第6个代码块下正文第2行 P127第2个代码块下正文第2行 这个uploads视图 这个get_file视图
4.4.4.4 P128 代码清单4-16 “检查文件类型”注释下第一行多余缩进4格 笔误 18.10.18
5 P139 第 1 个代码块 $ flask run 上面添加一行 $ flask initdb # 初始化数据库,后面会详细介绍 疏漏 19.1.5
5.4.1.1 P147 ”Create“小节第1个代码块第4行 'DON'T BELIEVE……' 'DON\'T BELIEVE……' 代码错误,漏掉转义符号
5.4.1.2 P150 表5-7下的第1个代码块 Note.body='SHAVE' Note.body == 'SHAVE' 代码错误,少了1个等号 18.9.22
5.4.2 P153 代码清单 5-5 删掉第 3 行,最后一行删除括号中的, form=form 代码未更新 18.12.24
5.4.2 P155 代码清单 5-8 第 3 行 DeleteForm() DeleteNoteForm() 代码未更新 18.12.24
5.5.2.3 P160 ”建立关系“小节第1个代码块 第2行插入ham.author_id = 1 省略步骤,可加可不加 18.9.22
5.5.2.4 P162 代码清单5-11第8行 title = name = 笔误 18.9.26
5.5.2.4 P163 ”建立双向关系“小节第3个代码块第6行 it.writer = writer it.writer = king 笔误 18.9.26
P164 第 2 段第 2 行和第 3 行两处 backref() 函数 db.backref() 函数 优化 19.3.7
P164 第一个代码块第 3 行 backref=backref(...) backref=db.backref(...) 错误 19.3.7
5.5.3 P164 代码清单5-13上面 都定义在“多”这一侧,即City类中 都定义在“多”这一侧,即Citizen类中 笔误
5.5.4 P165 代码清单5-14后面的代码块第6行 - 第6行>>> china.capital = beijing移动到第3行 笔误
5.5.4 P165 代码清单5-14后面的代码块第8行 <Capital 1> <Capital u'Beijing'> 笔误
5.5.4 P166 代码清单5-14后面的代码块第10行,166页第2行 u'China' <Country u'China'> 笔误
5.5.4 P166 代码清单5-14后面的代码块第11行,166页第3行 name'Tokyo' name='Tokyo' 笔误 18.9.26
5.7.1.2 P174 ”delete-orphan“小节上面第1个代码块第1行 Post.quer2y.get(2) Post.query.get(2) 审校错误
5.7.2 P176 代码清单5-18下第1行 targe target 笔误 18.9.28
5.7.2 P177 最后1个代码块上面 参数name 参数named 笔误 18.9.28
5.7.2 P177 最后1段提示文字 监听函数时, 监听函数。这时 编辑瞎改稿子 18.9.28
5.7.2 全章节多处 listen_for listens_for 笔误 18.9.28
6.1.1 P181 代码清单6-1 第9-13行末尾漏掉分割参数的逗号 笔误 18.10.27
7.3.2 P208 第1个代码块第6行 render_form(form),action=request.full_path render_form(form, action=request.full_path) 审校错误,右侧关闭括号位置出错
7.3.2 P208 表7-2上面段落的最后一句话 quick_form() render_form() 笔误。历史遗留问题 18.9.28
7.4.3 P213 最后 1 个附注段落 bootstrap.bundle.min.css bootstrap.bundle.min.js 笔误 18.12.5
8.1.3 P229、P231 代码清单8-2、8-4 单个蓝本变量名称均应为foo_bp形式,比如admin应为admin_bp 笔误 18.9.24
8.2.1.1.(4) P235 代码清单8-7后面第1个代码块第3行 db.relationship('Comment', backref='post', cascade='all') db.relationship('Comment', back_populates='post', cascade='all, delete-orphan') 笔误
8.2.3 P247 代码清单8-19第2行 from wtforms from wtforms.validators 笔误 18.10.27
8.3.1 P252 第1个代码块最后1行 'index.html' 'blog/index.html' 笔误 18.10.06
8.3.1 P252 代码清单8-24文件路径 templates/index.html templates/blog/index.html 笔误 18.10.06
8.3.5 P263 第1个代码块第6行 Comment.query.with_parent(post) Comment.query.with_parent(post).filter_by(reviewed=True) 笔误 18.9.26
P263 代码清单 8-29 第 2 行 {{ comments|length }} Comments {{ pagination.total }} Comments <!-- 使用 pagination.total 获取分页条目总数 --> 优化 19.3.5
8.3.5 P264 代码清单 8-29 倒数第11行 Reply</a> Reply</a></div> 笔误 18.12.6
8.3.5 P265 图 8-8 上的代码块第2行 {{ comment.replied.author.name }} {{ comment.replied.author }} 笔误 18.12.6
8.3.7 P267 代清清单8-31第4行 comment.photo_id comment.post_id 笔误 18.9.26
8.3.7 P267 代清清单8-31下方正文第2段最后一行的URL中 photo post 笔误 18.9.26
8.3.7 P268 第1行 photo show_post 笔误 18.12.6
8.4.1 P272 第1个代码块最后1行 >>> False >>> True 笔误 18.10.27
8.4.1 P273 纸书该页第 2 个代码块,电子书 8.4.2 上面倒数第 2 个代码块。第 5、7 行 check_password validate_password 笔误 18.12.6
8.5 P275 “8.5”小节下第2个代码块第2行 LoginManager(app) LoginManager() 代码错误 18.12.23
8.5.3 P278 代码清单8-37代码块第1行 from flask_login import logout_user from flask_login import logout_user, login_required 完善导入 18.12.6
8.6 P282 代清清单8-38第4行 '400.html' 'errors/400.html' 笔误 18.10.27
8.7.1 P285 代码清单8-40下的正文第1行 manage_category.html manage_post.html 笔误 18.10.27
8.7.1.2 P288 代码清单8-42代码块倒数第2行 .show_post blog.show_post 笔误 18.12.6
8.7.1.3 P290 第1个代码块倒数第5行 .show_post blog.show_post 笔误 18.12.6
8.7.2 P292 代码清单8-44代码块倒数第1行 .show_post blog.show_post 笔误 18.12.6
8.7.2.2 P294 代码清单8-45代码块下正文第1行 Ture True 笔误 18.12.6
9.1.1 P302 9.1.2 标题上面的代码块 bluelog myapp 笔误 18.12.6
P309 代码清单 9-2 倒数第 6 行 field.data field.data.lower() 优化 19.3.5
9.3.2.2 P314 代码清单9-6代码块最后1行 url_for('.resend_confirmation') url_for('.resend_confirm_email') 笔误 18.12.12
9.3.3 P315 代码清单9-8下的正文第2段第2行(纸书该页最后1行) auth.resend_confirmation auth.resend_confirm_email 笔误 18.11.5
P332 图 9-4 下面第 2 行 dropzone_upload 视图 upload 视图 错误 19.3.5
9.5.3 P334 9.6 小节上面的代码块最后一行模板字符串 home/upload.html main/upload.html 遗留代码未更新 18.12.17
9.5.3 P334 代码清单9-19后面的代码块倒数第二行 photo.save() db.session.add(photo) 换行 db.session.commit() 遗留代码未更新 18.10.27
P360 代码清单 9-41 倒数第 2 行和倒数第 8 行两处 self.collected Collect.query.with_parent(self) 错误 19.3.5
P363 代码清单9-45 倒数第 6 行 collections collects 错误 19.3.5
P363 代码清单9-45 第 3 行 ... import user_card with context %} ... import user_card %} 提前 19.3.5
P367 正文第 3 行 if_following() is_following() 错误 19.3.5
9.8.3 P363 代码清单9-45代码块倒数第6行 {% if collections %} {% if collects %} 笔误 18.12.10
P368 第 2 段第 2 行 去掉“和 is_followed_by()” 多余内容 19.3.5
9.9.4 P371 代码清单9-53的路径 albumy/templates/profile_popup.html albumy/templates/main/profile_popup.html 笔误 18.12.21
9.9.4 P375 代码清单 9-56 下面的代码块第二行 id="followers-count" id="followers-count-{{ user.id }}" 笔误 18.12.21
9.11.2 P388 最后一行 渲染avatar.html模板 渲染change_avatar.html模板 笔误 18.12.10
9.11.2 P389 第一个代码块下正文第1行 avatar.html模板继承自settings.html模板 change_avatar.html模板继承自settings/base.html模板 笔误 18.12.10
9.11.2 P389 代码清单9-71代码块第1行 {% extends 'user/settings.html' %} {% extends 'user/settings/base.html' %} 笔误 18.12.10
9.11.2 P389 代码清单9-71倒数第4行 {{ render_form(crop_form) }} {{ render_form(crop_form, action=url_for('.crop_avatar')) }} 笔误 18.12.24
9.11 P385 代码清单9-66倒数第9行 {{ render_nav_item('user.notification_setting', 'Notification and Privacy') }} {{ render_nav_item('user.notification_setting', 'Notification') }} {{ render_nav_item('user.privacy_setting', 'Privacy') }} 代码与实际项目不符 18.12.24
P395 第 1 个代码块第 3 行 show_collections public_collections 错误 19.3.5
P396 提示段落 show_collections public_collections 错误 19.3.5
9.11.6 P397 代码清单9-79代码块倒数第5行 current_user current_user._get_current_object() 笔误 18.12.10
P406 代码清单 9-85 第 3 行 在这一行最后紧跟着括号添加(注意不要漏掉开始的英文句点) .strip() 优化,避免输入空格作为搜索词 19.3.5
P412 代码清单 9-90 倒数第 3 行 field.data field.data.lower() 优化 19.3.5
P417 文件目录树倒数第 6 行 app.py todo.py 错误 19.3.5
10.1.1 P420 第 2 小节第一个代码块第 7 行 {{ url_for('todo.clear_item') }}; {{ url_for('todo.clear_items') }}; 笔误 18.12.27
10.1.4 P425 第一个代码块第 6 行 jsonify(message='Invalid item body.'), 400 return jsonify(message='Invalid item body.'), 400 笔误 19.1.20
10.3.3 P447 第 1 小节/该页最后一个代码块 ... import api ... import api_v1 笔误 18.12.28
10.3.3 P447 第 1 小节/该页最后一个代码块 csrf.exempt(api) csrf.exempt(api_v1) 笔误 18.12.28
10.3.3 P453 代码清单10-13第一行中的methods参数 methods=['GET', 'POST'] methods=['GET'] 与原定的方法不一致 18.12.28
10.3.3 P453 代码清单10-13第二行中的第一个参数 '/token' '/oauth/token' 与实际项目不一致 18.12.28
10.3.3 P453 代码清单10-13第二行中的methods参数 methods=['GET'] methods=['POST'] 与实际项目不一致 18.12.28
10.3.5 P462 代码清单10-20倒数第4行 item.author, item.author.username, 笔误 18.12.28
10.3.6.3 P468 ”处理错误响应“小节第1行 app.error_handler app.errorhandler 笔误
11.3.2 P483 代码清单11-4倒数第2行 花括号"}"后添加一个逗号"," 笔误 18.12.28
11.3.2 P484 代码清单11-5第2行 $('message') $('.messages') 笔误 18.12.28
11.4.1 P491 该节最后一段正文的第1行 views包 blueprints包 笔误 18.12.29
11.4.3 P498 附注段落下方正文第2行 provide_name provider_name 笔误 18.12.29
11.4.3.5 P503 代码清单11-11中多处 get['XXX'] get('XXX') 审校错误
11.4.3.5 P503 最后一个代码块 access_token = resp.get('access_token') access_token = response.get('access_token') 笔误 18.12.29
11.4.3.6 P505 提示段落上方的代码块 @oauth_bp.route(...) @auth_bp.route(...) 笔误 18.12.29
11.4.4 P506 第二个代码块 所有resp 改为 response 笔误 18.12.29
11.4.4 P506 第二个代码块 增加 if response is not None:... else:...语句,具体参考源码 笔误 18.12.29
11.5.1 P508 代码清单11-12 '_messages.html' 'chat/_messages.html' 笔误 18.12.29
11.5.1 P509 代码清单11-13第5行 position === 0&& socket.nsp! == '/anonymous' position === 0 && socket.nsp !== '/anonymous' 审校错误,空格错误 18.12.29
11.5.1 P510 代码清单11-13倒数第6行 toast('No more messages.'); alert('No more messages.'); 此项目中未定义toast函数 18.12.29
11.5.3 P513~P514 513页7处,514页8处 所有的 Pyments 改为 Pygments,pyments 改为 pygments 笔误 18.11.18
11.5.4 P516 代码清单11-15第3行 'message' 'new message' 笔误 18.12.29
11.5.4 P516 代码清单11-15第6行 document.title = '(' + message_count + ') ' + document.title; document.title = '(' + message_count + ') ' + 'CatChat'; 消息数量会随着事件多次发生而不断追加在原标题前 18.12.29
11.5.5 P517 代码清单11-17第6行 data.name data.nickname 笔误 18.12.30
11.5.5 P517 代码清单11-17第7行 icon: ... icon: data.gravatar 笔误 18.12.30
11.5.5 P518 最后1个代码块第5行 '_message.html' 'chat/_message.html' 笔误 18.12.30
11.5.5 P518 最后1个代码块第6行 message_body html_message 笔误 18.12.30
12.3.1 P526 代码清单12-1 setUp方法最后面追加一行代码self.client = app.test_client() 代码缺失 19.01.05
12.3.2 P527 提示段落下方的正文第3行 assertEqual() assertTrue() 笔误 19.01.05
12.3.2 P527 代码清单12-3 test_404_page方法的注释 # 测试 400 错误页面 # 测试 404 错误页面 笔误 19.01.05
12.3.3 P534 代码清单12-6最后1个导入语句 from bluelog.models import User from bluelog.models import Admin 笔误 19.01.06
13.1.2 P550 代码清单13-1 @app.after_app_request @app.after_request 笔误 19.01.06
13.1.2 P550 代码清单13-1 两处current_app改为app 笔误 19.01.06
13.3 P557 命令行命令 $ cd cache $ cd assets 笔误 19.01.06
14.3.4.1 P569 代码清单14-1上方正文 register_logger() register_logging() 与实际源码不符 19.01.21
14.3.4.1 P569 代码清单14-1 register_logger() register_logging(app) 与实际源码不符 19.01.21
14.3.4.1 P569 代码清单14-1 RotatingFileHandler的第一个参数 os.path.join(basedir, 'logs/bluelog.log') 与实际源码不符 19.01.21
14.3.4.2 P570 代码块 register_logger() register_logging(app) 与实际源码不符 19.01.21
14.3.4.3 P571 代码清单14-2 register_logger() register_logging(app) 与实际源码不符 19.01.21
14.3.4.3 P571 代码清单14-3 register_logger() register_logging(app) 与实际源码不符 19.01.21
14.3.4.3 P571 代码清单14-3 5处os.getenv() app.config[] 与实际源码不符 19.01.21
14.4.3.1 P576 提示段落第二行 C/Users/Administrator/.ssh C:\Users\Administrator.ssh 错误 19.01.21
14.4.7 P584 倒数第2个代码块上方段落第5行 放在/etc/supervisord.conf路径下 放在/etc/supervisor/conf.d路径下 笔误 18.11.28
15.5 P611 正文第5行 在模板中提供load()方法资源。 在模板中提供load()方法加载静态资源。 句子不完整 19.01.21
15.5.1 P612 页面中部代码块 def load(css_url=None, js_url=None): def load(css_url=None, js_url=None, serve_local=False): 与实际源码不符 19.01.21
15.5.1 P612 页面中部代码块 if current_app.config['SHARE_SERVE_LOCAL']: if serve_local or current_app.config['SHARE_SERVE_LOCAL'] 与实际源码不符 19.01.21
15.5.1 P612 页面中部代码块最后1行中的filename参数 filename='js/share.min.js' filename='js/social-share.min.js' 笔误 19.01.21
15.6.1 P614 代码清单15-8中Share类的init_app方法中的blueprint Blueprint缺少static_folder和static_url_path参数 见代码清单15-5或项目源码 笔误 19.01.21
15.6.1 P614 代码清单15-8中Share类的init_app方法 没有将扩展添加到模板上下文 添加代码app.jinja_env.globals['share'] = self 代码缺少 19.01.21
15.6.1 P614 代码清单15-8中Share类的init_app方法最后1行第2个参数 True False 与实际源码不符 19.01.21
15.6.1 P615 代码清单15-7和代码清单15-8中Share类的create方法参数 addition_class=None addition_class='' 与实际项目不符 19.01.21
15.7.3 P625 最后1个代码块的第2行 git push origm git push origin 审校错误。另外,这一行下面的Github应为GitHub
16.2.4 P639 最后1个段落的第2行 在不基于线程、greenlet或单进程实现的并发服务器上 在不基于线程、Greenlet 或进程实现并发的服务器上 笔误 18.12.31
16.4.2 P649 最后1行 Flask.route()是Flask类的类方法 Flask.route()是Flask类的实例方法 笔误 19.1.1
16.4.2.1 P650 最后第2段正文第1行 view_function view_functions 笔误 19.01.21
16.4.2.2 P653 代码清单16-15中的注释 出于 处于 笔误 19.01.21
16.4.3.2 P659 第1段第3行 并将数据的字典名称设为'stack'。 并将储存上下文对象的列表名称设为'stack' 笔误 19.1.4
16.5 P680 附注第2行 Pocco风格指南 Pocoo风格指南 笔误 19.01.21

P412 添加 validate_username() 方法定义(同时添加到 1-1 勘误):

def validate_username(self, field):
    if field.data != self.user.username and User.query.filter_by(username=field.data).first():
        raise ValidationError('The username is already in use.')

P363 正文第 1 段,原文:

我们在第 3 章曾经介绍过 Jinja2 的上下文机制,因为 user_card 宏里使用了 Flask-Login 提供的 current_user 变量,所以我们需要在导入时使用 with context 指令显式声明包含上下文:

改为:

根据第 3 章介绍的 Jinja2 上下文机制,因为 user_card 宏后面会使用 Flask-Login 提供的 current_user 变量,到时你需要为相关的导入语句追加 with context 指令显式声明包含上下文:

P393 代码清单9-74 从该页第 3 行到第 8 行,原文为:

    if form.validate_on_submit() and current_user.validate_password(form.old_password.data):
        current_user.set_password(form.password.data)  # 重设密码
        db.session.commit()
        flash('Password updated.', 'success')
        return redirect(url_for('.index', username=current_user.username))

修改为:

    if form.validate_on_submit():  # 验证表单是否通过验证
        if current_user.validate_password(form.old_password.data):  # 验证旧密码
            current_user.set_password(form.password.data)  # 设置新密码
            db.session.commit()  # 提交数据库会话
            flash('Password updated.', 'success')
            return redirect(url_for('.index', username=current_user.username))
        else:
            flash('Old password is incorrect.', 'warning')  # 旧密码不对则显示提示

P409 代码清单 9-88 在第 2 行和第 10 行(@permission 开头的两行)上面分别插入新的一行,内容为 @login_required,比如:

@admin_bp.route('/lock/user/<int:user_id>', methods=['POST'])
@login_required
@permission_required('MODERATE')

P275 8.5 小节标题上面添加提示段落:

提示 在 fakes.py 脚本里的 fake_admin() 函数中,我们需要在 admin 对象创建后,为虚拟用户记录设置密码:admin.set_password('helloflask')。

不重要勘误

还有一些不重要的勘误没有在这里列出,详情访问勘误源码查看。