Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
312 lines (224 sloc) 10.4 KB

勘误表

文本错误

章节(页码) 原文 修改为 出处
第一章 Web框架介绍(P4) Fackbook Facebook @志荣
第2章 安装Docker(P11) 头部 Ubuntu xenial 14.04 (LTS) Ubuntu Trusty 14.04 (LTS) @伟忠
第2章 插件系统(P17) 头部 我们先安装pep-naming 我们先安装pep8-naming @tntC4stl3
第2章 autoenv(P24) 中间 echo "source source /home/ubuntu..." echo "source /home/ubuntu..." @刘一鹤
第3章 配置管理(P31) 尾部 app.config.from_envar('SETTINGS') app.config.from_envar('YOURAPPLICATION_SETTINGS') @凝霜
第3章 自定义URL转换器(P34) 中间 ...join(BaseConverter.to_url(value) ...join(super(ListConverter, self).to_url(value) @MrCJJW
第3章 响应(P38) 尾部 转换为一个请求对象 转换为一个响应对象 @logchi
第3章 模板继承(P50) 头部 ... 换成了 "Index" ... 换成了 "Index - My Webpage" @志荣
第3章 模板继承(P58) 尾部 ... ${ title() } ... ... ${ self.title() } ... @MrCJJW
第3章 本地线程(P75) 头部 ...chapter3/local.py ...chapter3/section4/local.py @tntC4stl3
第3章 使用上下文(P77) 尾部 ... 请求上下文与应用文 ... ... 请求上下文与应用上下文... @moling3650
第3章 从零开始实现一个文件托管服务(P81) 中间 UNIQUE KEY filehash (filehash), UNIQUE KEY filehash (filehash) @casper2dd
第3章 短链接页(P88) 尾部 ...chapter3/section7/app.py ...chapter3/section5/app.py @志荣
第5章 使用Ajax(P137) 尾部 error = 'Password must be at least 5 ...' error = 'Username must be at least 5 ...' @特特特~
第6章 Nginx配置 尾部 root /home/ubuntu/web_develop/static;\n} root /home/ubuntu/web_develop/static; @伟忠
第6章 通过Gunicorn启动Flask应用(P148) ... -b :9000 ... -b :8000 @Reo-LEI
第6章 Libmc安装配置(P151)尾部 ... 转移到写 ... 转移到另 @zyt312074545
第6章 善用组合式的大文档(P177) 中间 ... 311□s per loop ... 311µs per loop @Silence-WWT
第6章 高可用方案(P180) 复制(repliaction) 复制(replication) @伟忠
第7章 配置Supervisor(P187) 中间 $CWD/etc/supervisord.conf $CWD/supervisord.conf @伟忠
第7章 Role和Galaxy 中间 比如之前看到的redis-3.8和redis-3.0 比如之前看到的redis-2.8和redis-3.0 @志荣
第12章 IPython交互模式(P338) 中间 ... best of 3: 9.2 □s per loop ... best of 3: 9.2 µs per loop @Abirdcfly
第12章 常用的Magic函数(P341) 中间 ... sys: 4 □s, total: 4 □s ... sys: 4 µs, total: 4 µs @Abirdcfly
第12章 常用的Magic函数(P341) 中间 ... Wall time: 8.11 □s ... Wall time: 8.11 µs @Abirdcfly
第12章 free(P360) 尾部 - buffers/cache表示可用内存 + buffers/cache表示可用内存 @hezhiming
第13章 Python并发编程(P383) 中间 Referfer Referer @志荣
第13章 Python并发编程(P384) 头部 Referfer Referer @志荣
第13章 使用Gevent(P394) 中间 def get_random_proxy(cls): def get_random(cls): @志荣
第13章 使用多进程(P401) 尾部 ... 29.7 □s per loop ... 29.7 µs per loop @伟忠
第13章 使用多进程(P401) 尾部 ... 44.6 □s per loop ... 44.6 µs per loop @伟忠
第13章 使用多进程(P402) 头部 ... 44.1 □s per loop ... 44.1 µs per loop @伟忠
第13章 使用多进程(P402) 头部 ... 86.1 □s per loop ... 86.1 µs per loop @伟忠
第14章 contextlib(P422) 尾部 __exit_ __exit__ @伟忠
第14章 collections(P428) 尾部 快速计算的计时器工具 快速计算的计数器工具 @ethan-funny
第14章 使用CFFI(P444) 尾部 1. ABI的in-line模式。ABI模式模式不需要... 1. ABI的in-line模式。ABI模式不需要... @志荣

代码错误

  1. 第4章 Flask-Migrate(P98) 尾部。出处 @凝霜

原文:

db.init_app(app)

migrate = Migrate(app, db)

修改为:

db.init_app(app)
import users  # noqa
migrate = Migrate(app, db)
  1. 第6章 实时统计(P166)中间。出处 @moling3650

原文:

import redis

ACCOUNT_ACTIVE_KEY = 'account:active'

r.flushall() # 为了测试方便,每次启动后先清理Redis

修改为:

import redis

ACCOUNT_ACTIVE_KEY = 'account:active'

r = redis.StrictRedis(host='localhost', port=6379, db=0)
r.flushall() # 为了测试方便,每次启动后先清理Redis
  1. 第3章 在Flask中使用SQLAlchemy(P71)中间。出处 @tntC4stl3

原文:

```
class User(db.Model):
    __tablename__ = 'users'
    ...
```

修改为:

```
class User(db.Model):
    __tablename__ = 'users2'
    ...
```

通过修改表名字避免 [Issue #15](https://github.com/dongweiming/web_develop/issues/15) 的错误
  1. 第3章 使用表达式(P66)尾部。出处 @tttimit

原文:

```
if users.exists():
    users.drop()

def execute(s):
```

修改为:

```
if users.exists():
    users.drop()
users.create()  # 创建表

def execute(s):
```
  1. 第10章 服务端实现(P295)中间。出处 @志荣

原文:

@classmethod
def create_by_upload_file(cls, uploaded_file):
    ...
return rst

修改为:

@classmethod
def create_by_upload_file(cls, uploaded_file):
    ...
    return rst
  1. 第7章 应用部署Fabirc(P193)中间。出处 @志荣

原文:

from fabric.api import run

修改为:

from fabric.api import run, local
  1. 第11章 使用Mapreduce(P308)头部。出处 @志荣

原文:

import import bz2

修改为:

import bz2

文件权限

感谢 @ryanli1994指出,写的时候按照之前我的记忆的错误信息写了。 原文是:

我们使用位运算做权限控制。位运算在Linux文件系统上就有体现,一个用户对文件或目录所拥有的权限分为三种:"可读(1)"、"可写(2)"和"可执行(4)",它们之间可以任意组合:有可读和可写权限就用3来表示(1
+ 2 = 3);有可读和可执行权限就用5来表示(1 + 4 =
5),三种权限全部拥有就用7表示(1 + 2 + 4 = 7)。为什么选择1、 2、4这样的有规律的数据呢?先看看下面的例子:

我把可读和可执行记反了,应该是可读(4),可执行(1),修改为:

我们使用位运算做权限控制。位运算在Linux文件系统上就有体现,一个用户对文件或目录所拥有的权限分为三种:"可读(4)"、"可写(2)"和"可执行(1)",它们之间可以任意组合:有可读和可写权限就用6来表示(4
+ 2 = 6);有可读和可执行权限就用5来表示(4 + 1 =
5),三种权限全部拥有就用7表示(1 + 2 + 4 = 7)。为什么选择1、 2、4这样的有规律的数据呢?先看看下面的例子:

正则匹配代理地址问题

感谢 @loveQt 指出。我完成正则匹配代理地址的时候距离现在已经过了半年,有些代理地址不可用,有些解析的姿势不对。具体的可以看 Issue #10。再隔一段时间可能又会变动,所以建议大家学习会方法就好了,具体的实现要对症下药啦。

方法语义的说明

感谢 @Pyclearl

在第5章 合理使用请求方法和状态码(P129)的「方法语义的说明」表中:

有这样一段:

PUT 用于完整的替换资源或者创建指定身份的资源,比如创建 id 为 123 的某个资源
    1. 如果是创建了资源,则返回 201 Created
    2. 如果是替换了资源,则返回 200 OK

PATCH 用于局部更新资源
    1. 完成请求后返回状态码 200 OK
    2. 完成请求后需要返回被修改的资源详细信息
    3. 完成请求后需要返回被修改的资源详细信息

其中有一句位置不对,应该是这样:

PUT 用于完整的替换资源或者创建指定身份的资源,比如创建 id 为 123 的某个资源
    1. 如果是创建了资源,则返回 201 Created
    2. 如果是替换了资源,则返回 200 OK
    3. 完成请求后需要返回被修改的资源详细信息

PATCH 用于局部更新资源
    1. 完成请求后返回状态码 200 OK
    2. 完成请求后需要返回被修改的资源详细信息

本书最大的错误

感谢 @guyskk Issue 20反馈。我对LocalProxy的理解有问题, 虽然在本书的例子中没有问题,但是使用的姿势是错误的。我们先复现下问题:

首先给 chapter3/section4/app_with_local_proxy.py 中的 get_current_user 函数加个调试信息:

def get_current_user():
    print 'call'
    users = User.query.all()
    return random.choice(users)

接着进入IPython环境,执行如下命令:

❯ ipython
In [1]: from app_with_local_proxy import *
In [2]: ctx = app.test_request_context()
In [3]: ctx.push()

In [4]: current_user.name, current_user.name
call
call
Out[4]: (u'admin', u'xiaoming')

In [5]: current_user.name, current_user.name
call
call
Out[5]: (u'dongwweiming', u'dongwweiming')

In [6]: current_user.name, current_user.name
call
call
Out[6]: (u'admin', u'xiaoming')

大家看到了吧,current_user相当于每次都要从数据库里面取一次结果。

我们分析下源码

def __getattr__(self, name):
    if name == '__members__':
        return dir(self._get_current_object())
    return getattr(self._get_current_object(), name)

每次调用current_user.XX都会触发__getattr__,而直接进行 _get_current_object 的执行, 也就是都会执行了一次get_current_user。

我以前以为单独用LocalProxy就可以了。其实还是得LocalProxy和LocalStack一起用:

from werkzeug.local import LocalStack, LocalProxy


_user_stack = LocalStack()


def get_current_user():
    top = _user_stack.top
    if top is None:
        raise RuntimeError()
    return top

current_user = LocalProxy(get_current_user)


@app.before_request
def before_request():
    users = User.query.all()
    user = random.choice(users)
    _user_stack.push(user)


@app.teardown_appcontext
def teardown(exc=None):
    _user_stack.pop()