《Flask 入门教程》第三版发布

最近手里堆积的事情越来越多,两本书的进度被日常工作挤压,于是非常想有一些小小的进展来缓解焦虑。刚好有读者来反馈《Flask 入门教程》不兼容最新版本 Flask 的问题,所以就花了一天时间来更新这个教程。

发布上个版本已经是 2019 年的事情了,这次针对 Flask 最新的 2.1.3 版本做了改写,同时不再兼容 Python 2.7 版本。优化了前面几章的一些内容,后面的章节需要改动的地方不多,就没有认真去更新。另外还顺便用 MkDocs 和 Netlify 把教程重新部署到了 tutorial.helloflask.com 上,后续会废弃掉 GitBook。

过去三年里,这本电子书大概给我带来了五百块的收入(主页上有一个自愿点击的「付费支持¥10」的按钮)。虽然不多,但是时不时收到到账提醒和鼓励的话还是很开心的。感谢每一位付款的读者!

当然,给我带来更大成就感的是看到读者分享他/她通过阅读这个教程写的程序,这是一种给别人带来成就感的成就感——元成就感。

累死的人

每年临近小满的一周时间里,在凌晨两点的苏北农村,每个村子的村口或是某个地方都会有乌泱泱一群人在等车去各处「出蒜」(把蒜从土里拔出来)。这些人大多是四十岁到七十岁、甚至还有八十多岁的中老年人,往年或许还有三十多岁的年轻人,但现在已几乎见不到。

在漆黑的夜里,一辆辆三轮车里挤满了人,他们蜷缩着打着盹,被拉往各处田地。这是村民们一年里少有的能够挣到「大钱」的时候。麦子、水稻和玉米的收获都被收割机包揽,而空有一双手的人,只剩下出蒜、栽稻这两次机会。每年其他时间在各处的杂活,一天能挣五十到一百块,而出蒜一天最少也是两百块起步(蒜的收获赶时节,晚了蒜瓣就会散开)。如果你能干得更快的话,选择包亩而不是包天,每亩甚至能挣到四五百块。

附近村子有位七十多岁的老人,以包亩的方式,一天挣到了九百块。老人很自豪,还遗憾没能凑个整数挣到一千块。但这种自豪和遗憾并没有持续多久。收工回到村子,她下了车没走几步就倒了下去,再没有起来,手里依然还攥着那九百块。九百块,意味着一个人拔了近两亩地的蒜。在三十度的烈日下,坐着或跪着,在地上一点一点挪动,把上万根蒜从地里一一拔出来、甩掉泥土。最后只剩下九百块。

APIFlask 1.0 版本发布

2023/7 Update: APIFlask 2.0 版本有一些变动,详见这篇文章


APIFlask 是一个基于 Flask 和 marshmallow 实现的 API 框架(详细介绍见这篇文章)。经过一年的迭代,终于在这个月初为它发布了 1.0 版本(撒花~)。

1.0 版本主要带来了三个新功能:

  • 支持处理文件上传,新增 File 字段
  • 支持使用外部的认证库
  • 添加了几个新的请求解析位置:json_or_formform_and_files 以及 path

一个 API 变动(breaking change):

  • inputoutputdocauth_required 这四个装饰器移动到了程序实例和蓝本实例上(比如 app.input() / bp.input())。

还有一些代码上的修正和优化,具体可以在变更日志里看到。欢迎更新试用和提交反馈:

$ pip install -U apiflask

在代码之外,也有一些变动和改进:

如果你正在用它开发你的个人项目或是公司项目,欢迎在这里留言告诉我们(我)!

回不去的上海

过完年回到上海的这段时间,陆续凑齐了所有的东西。买了新的床、床垫和衣柜(感谢房东出了 80% 的钱),买了二手的桌子、椅子、台灯、自行车……从家里寄来一些炊具和餐具。从超市买了米、油、鸡蛋和面条。唯独缺一把菜刀。快递不让寄刀,坐火车行李也不让带刀,附近的几个小超市也没找到。来上海后做的第一顿饭,是用勺子切了葱花炒的蛋炒饭(配料是在便利店买的沙拉蔬菜包):

blank

虽然房子空间越来越小,还是缺很多东西,离我完美的上海新生活还差得很远。我想念我的大显示器、键盘鼠标以及各种有用没用的小物件。所以我最终决定在三月的第一个周末回家,把它们带过来。本打算在家呆一周就回来(一周后的车票都买好了),结果一直呆到了现在,侥幸躲过很多次核酸检测和抢菜团购。如果我还在上海,估计会在小区快要被封的时候满大街买刀吧。

回家还有另一件事想做。过年在家的时候,我向身边的亲戚朋友了解了当地在八九十年代的妇女拐卖情况(我家在徐州另一个县的农村)。但是很快假期就结束了,了解到的信息很有限,只在 Twitter 上写了几条推文作为总结。这次回去想深入采访当地农村人口拐卖的事情,写一篇文章出来。因为某些原因,这件事没有做成。

到家的前十四天,我的行程码上有上海的记录,去商场要登记,后来看电影都被要求提供 48 小时核酸证明(就是让我退票的意思)。我如果再晚两周回来,就要享受各种防疫套餐,什么 3+11 啦,7+7 啦,14 天内要做 9 次核酸。

随着上海开始封城,越来越多离谱荒谬的事情发生。每天看着这些信息感到愤怒又无力。这些因为防疫政策而不是病毒本身导致的饥饿、伤害、苦难和死亡是有意义的吗?现在这样混乱、极端的「清零政策」到底是为了什么?没人知道答案。广播里只是一味地重复:让我们众志成城,万众一心,大家吃一点苦,我们迟早会战胜病毒!更加悲哀的是,这些事情在每一个城市都可能发生、已经发生、甚至正在发生。

网络上到处都是煽动仇恨和对立的垃圾文章,比如前一阵那篇国本雄文,看到最后我都恍惚以为文革还没结束。另一方面,求救、揭露现实的文章和评论只会被封禁和删除。连公共媒体都热衷于转发各种意大利英国阴谋论网站的假新闻,你还能指望那些写手们弄出什么正常东西来。

现在仿佛又回到了呆在家里没工作的日子,不知道哪天能回上海。说不定还没有等到回上海那天,就要被封锁在徐州了,对于自己的生活现在谁能说得准呢。

为了薯条、月亮和六便士

前两天偶然翻到《月亮和六便士》的豆瓣条目,看到我刚好在十年前的这一天标记了这本书。当时写下了这条短评:

blank

不知道那位老梆子现在是否还健在?那位像座小山一般、每天在楼道里踱来踱去、以贬低打骂学生为生的教导主任。

我们活着大概就是为了薯条、月亮和六便士。薯条在很远的码头上,而月亮和六便士不过是随时会被没收的东西。而被没收的月亮和六便士,也许我哪天可以拿回来。

如果你在 macOS 上无法访问 Flask 程序

如果你用 macOS 开发 Flask 时无法正常访问程序,或是使用 ngrok 等内网穿透工具时映射的公网地址无法访问,大概率是因为 macOS 新版本(Monterey)的变动导致。你会在页面上看到类似下面的错误信息:

Access to 127.0.0.1 was denied.

You don't have authorization to view this page.

HTTP ERROR 403

或是在执行 flask run 命令时看到类似下面的报错:

OSError: [Errno 48] Address already in use

首先确认你的 macOS 版本是不是 Monterey(左上角 Apple 图标 – About This Mac)或是之后的版本,如果不是那么问题应该和本文无关。

简单来说,新版本的 macOS 上 localhost 5000 端口被一个叫 AirPlay Receiver 的服务占用了。而 Flask 内置服务器默认就运行在 5000 端口,所以会造成端口冲突。当你通过将 host 设为 0.0.0.0 指定 Flask 的内置服务器对外可见,或是使用内网穿透工具时,会发现程序无法访问(有时未设置对外可见也会遇到这个问题)。

最简单的解决方法是关掉这个服务:

  • 系统设置(System Preferences) > 分享(Sharing) > AirPlay Receiver > 取消勾选

或是更改 Flask 开发服务器默认的端口(比如改成 8000)。在执行 flask run 命令时使用 -p/–port 选项可以自定义端口:

$ flask run -p 8000  # 或 flask run --port 8000

或是在执行 flask run 之前通过环境变量 FLASK_RUN_PORT 设置:

$ export FLASK_RUN_PORT=8000 # macOS/Linux
> set FLASK_RUN_PORT=8000 # Windows CMD
> $env:FLASK_RUN_PORT=8000 # Powershell

在 Werkzueg 2.1.0(2022/3/28 发布)版本,如果你执行 flask run –host=0.0.0.0 时检测到了端口被占用,会直接显示相关的错误提示:

$ flask run --host=0.0.0.0
Address already in use
Port 5000 is in use by another program. Either identify and stop that program, or start the server with a different port.
On macOS, try disabling the 'AirPlay Receiver' service from System Preferences -> Sharing.

本文源于去年十一月发的一条推文,最近经常碰见有人遇到这个问题,所以转成这篇文章发出来。

画了一幅涂鸦,送给苹果公司:

blank

2021 年总结

今年一整年都呆在家里。书断断续续地写,但一本也没写完。最后两个月,因为钱花光了,终于下定决心开始找工作,最终在 2021 年最后几天拿到第一份工作的 offer,结束了近五年的自由职业状态。这个 offer 给这一年画上了一个圆满的句号,我也从此跳进这个圈,开始一种稳定循环的新生活。

先从好坏两方面总结今年。

Good

Bad

  • 三个年目标全部落空:两本书都没有写完,APIFlask 也没有发布 1.0 版本
  • PyCon US 2021 演讲议题申请没通过(备选的闪电演讲通过了)
  • 找工作收到拒信若干封

今年尝试了很多新鲜的东西,比如第一次录播客、第一次参加圆桌、第一个英文演讲、第一场足球赛、第一份工作。今年也开始更活跃的使用 Twitter,并且重新捡起来被荒废的 PyCon China 账号。

从下半年开始,经常感觉很焦虑。一方面是因为写书进度缓慢,不知不觉一年又已经过半。另一方面是因为收入——今年拿到的稿费很少,加上不想浪费时间做外包,因此收入锐减。既没有挣到钱,也没有做成事,在这些压力下常常心情也不好,所以自觉失败。

时间记录

今年没有再完整的记录时间花销,去年一年的实验已经让我了解自己的时间花销情况。再继续意义不大,一来记录本身就会浪费很多时间,二来过于追求效率和生产力会让人感到疲惫。因此这一年只记录了一些重要的活动分类。

总记录时间一共 1815 小时(约占全年时间的 20%),其中前三项分别是:

  • 开源项目:535h 50m
  • 找工作:204h 37m
  • 游戏:200h 55m

完整的数据如下:

blank

开源项目时间基本都集中在年初,当时花了几个月时间开发 APIFlask。年中 Flask 2.0 发布后帮忙修了几个 bug,年底给 Bootstrap-Flask 发布了支持 Bootstrap 5 2.0 版本。

找工作集中在最后两个月,不过投入时间最多的几个职位都没有拿到 offer。晚点会在另外一篇文章详细记录找工作的经历和感想。

游戏时间大部分花在了最后一个月开始玩的《塞尔达传说:荒野之息》。这个游戏在年初就和 Switch 一起买了,因为怕花掉太多时间一直没有玩。在找工作等待面试答复的漫长煎熬过程中,什么事情都不想做,所以躲到游戏里。

时间花销排在第六位的写书,本来应该是我的主业。之前完成两本书的阻力很多,比如要解决收入问题,要整理第一版的旧文件排版,还要完善 APIFlask 并发布 1.0 版本。而目前来说,完成它们的最大阻力则是时间太少。工作之后,除了周末两天,每天只有下班后六点到十一点这个时间段才能做自己的事情。再去掉吃饭时间以及偶尔的散步、看电影、玩游戏、做开源项目……不过好在我可以在这个时间段专心做事,没有收入焦虑。

时间花销排在最后的是阅读。今年又没读什么书,记录在案的阅读时间只有 55 分钟……想想大概是在读《非暴力沟通》,不过读了个开头就用来垫枕头了。枕了那么久,也许会吸收到一点点灵气吧。

新年目标

因为 2021 年的三个年目标一个也没完成,所以 2022 年仍然是这三个:

戴尔恶魔城居民+1

一周前来到上海,今天顺利入职戴尔 EMC(别名恶魔城),我的找工作历程至此终于完结了。对新工作和新生活充满期待。

blank

来到上海找了三天房子,最终选了一个离公司 483 米、在意向清单里价格最高的房子。公司周围都是三十多年房龄的破旧小区,一室的价格应该够在家乡县城租七室三厅。好处是离体育场(江湾体育场)和商场(五角场)也只有五百米上下,有很多好吃好玩的地方。

第一次在公司上班,到处都很新鲜,不过第一天不太好意思到处闲逛。中午和同事们一起吃饭散步,感觉像是回到上学的时候。今天帮助我入职的 buddy 是我的读者,不过他今天在家办公并没有来(失职的入职 buddy)……其他同事还把我的书从他的工位拿过来「指认作者」,哈哈。

说到这本书,自然就想起来第二版的截稿日期就快到了,心情突然沉重。各位潜在的读者稍安勿躁,明早起床就开始写书!

Flask 工作机会

最近在找工作,发现了一些和 Flask 相关的工作机会。因为目前并没有一个收集 Flask 相关职位的平台,所以就在 GitHub 上创建了一个仓库来分享这些职位,希望可以帮到其他正在找工作的 Flask 用户:

职位以 issue 的形式列出。目前列出来的几个都是和 Flask 关联非常大的职位,这里面有的我投递了申请(比如戴尔),有的收到了拒信(比如育碧),有的收到对方的面试邀请(比如 Dropbase)但并没有通过面试……所以这些职位都在开放中,如果你在找工作的话,尽管投递简历。同时也欢迎在投递之后回来分享面试进度/经历/经验。

如果你所在的公司正在招聘 Flask 相关的职位,或是你在未来发现某个公司发布新的 Flask 相关的职位,欢迎在这个仓库创建 issue 分享。创建 issue 时请遵循下面的格式要求:

  • 在标题注明「公司-职位-城市」,远程工作统一使用「Remote」。
  • 把职位描述里的 requirements 部分贴到 issue 内,并在最后给出职位详情链接。
  • 建议发布和 Flask 技术栈关联比较大的职位,不过 Python 相关的职位也欢迎。

顺便说一句,有很多 Python 相关的工作都会要求掌握某一种 Python Web 框架,类似这样的描述:

  • Good knowledge of web frameworks like Flask, Django, etc.

即使对应职位实际不用 Flask 也不用担心,当你掌握了某一种框架,其他的框架学习起来会非常容易,因为大部分的概念都是相通的。而面试时也会根据你擅长的框架提问,所以尽管申请就好。

祝大家都能找到自己喜欢的工作。

开始找工作

在过去的几年里,虽然也有过几次找工作经历,但都是被别人推荐后去试一试,结果无一例外全失败。不过,除了学历、英语和申请时间点的问题,这些失败的职位大都不是我的强项所在(Python/Flask)。这一次我决定主动起来,以自己擅长的方向出发,努力在 2022 年之前找到人生的第一份正式工作。

我想是时候了。我在写书这件事上很难挣到足够多的钱,不应该再抱有写出畅销书的幻想(至少短期内不可能),所以找工作才是下一步应该要做的事情,这样才能让写书和做开源项目这些事变得可持续。

我会在拿到第一个 offer 的时候开下面的薯片庆祝!

blankP.S. 这盒薯片的过期时间是 2022 年 7 月 14 日。两个星期以来,它已经陪伴我经历四场面试,收到一封拒信。


2021/12/29 Update:

终于拿到了 offer,撒花!开吃!

blank


2022/1/21 Update:

已顺利入职戴尔 EMC

拍抖音不尴尬

今晚出去散步,火车经过的时候我举起手机录像。身边有几个路人走过来,这让我感觉尴尬,像是手机烫手。但是当走过的大妈对身边的同伴说——“拍抖音的”,我一下就释然了。

想起以前经常拿着相机到处拍。骑着自行车在各种村子里闲逛,拍那些不一样的房子,破旧的,畸形的,门前有着小花园的,墙上写了毛笔字的。拍那些在门口发呆的老头老太太们。也会在某个城市暴走,拍躺倒在路边的工人(别担心,只是在午睡),破旧的没人注意的角落,旧市场沿街二楼趴在窗户边的孩子。这些时候就会很在意别人的眼光,感觉自己像是一个在人流里停下,让时间变慢的那个人;或者是升旗时迟到,需要穿过所有的人走到第一排的人。担心被被拍摄的人发现,也担心被路人发现。

一定是这种尴尬心理阻碍了我成为一个青年摄影家或是纪录片导演。当然,也可能罪魁祸首是高二偷走我装满照片的 U 盘的凶手,毕竟我青年时代的伟大作品都在那个 U 盘里 :D。

而有的人就可以完全做到不在意别人的眼光。在南京的时候,有一次步行去超市,在路上看到前面的人举着手机走路,走着走着就停住了,走过很远回过头看他还在那里,仿佛被人凭空点了穴一样。不知道他在那里站了多久,会不会一抬头发现天已经黑了。很显然,他有自己的世界和宇宙。我很羡慕。

我想这种不在意别人目光的能力是需要培养的。这样的锻炼我不常有,还记得我做的最大胆的一次。那是和女朋友刚刚认识的时候。那一天是 8 7 日,之所以记得那么清楚,因为我们把这一天叫做哑巴日,不过后来就慢慢丢失了这个传统。那天我们在车站见了面,见面之后不知道为什么我们都没有说话,就看着对方傻笑。于是我提议今天作为哑巴日,谁都不许说话,谁先说话就算谁输。我们到了肯德基,面对面坐着,不说话,憋着笑比划手势。然后我决定迎接更大的挑战,所以主动去点餐,对着菜单指着一个汉堡,比出「2」的手势,店员错愕后点头并且投来关切的眼神。我们在沉默中吃完了汉堡。不过那天谁先说了话我已经记不起来了。

开始写作《Flask Web 开发实战》第二版

Flask Web 开发实战》第二版已经开始写作,计划在今年完成这本书。那本《Python Web API 设计与开发》一直拖着没写完,让我感觉很焦虑。因为拖得太久,HelloFlask 群聊里已经开始谣传我转行送快递了 :D。

所以我想先把《Flask Web 开发实战》新版完成,这样这一年我至少完成了三个年度目标中的一个。「优化」是我擅长和喜欢做的事情,除了要用 Word 写之外,我很乐意给这本书写新版。

内容变动

在各处收集了很多读者反馈和建议,综合我自己的规划,第二版主要有这些大的变动:

  • 不再兼容 Python 2.7,基于 Python 3.x、Flask 2.x。
  • 把大部分知识点集中到第一部分,让第二部分的示例程序内容更精简。
  • 第 1 章使用 pip+venv(+pip-tools) 或 PDM 替代 Pipenv,增加更多对 PyCharm 的介绍。
  • 第 6 章使用 Flask-Mailman 替代 Flask-Mail(同时引入介绍一些新的扩展,比如 Flask-Admin)。
  • 删除第 9、10、11 章(删除后 3 个实战项目),仅保留 Sayhello 和 Bluelog,删减项目的部分功能会合并到这两个项目。
  • 第 10 章删除实战项目,只保留 Web API 开发部分,会新引入 marshmallow 和 APIFlask。
  • 第 12 章添加异步任务相关内容。
  • 第 14 章添加 Docker 相关内容。
  • 删除第 15 章(Flask 扩展开发)。
  • 删掉一些在国内访问有问题的服务介绍,比如 SendGrid(第 6 章)、Heroku(第 14 章)、Twitter 第三方登录(第 11 章)。

大部分内容变动是在做删减,第一版 704 页,第二版内容会删减到 450 页左右。

代码片段

第一部分每一章都有一个示例程序,读者需要切换到每一个子文件夹运行,如果同时自己编写练习代码的话,很容易产生混乱。所以新版的考虑是,第一部分的示例程序不再给出可运行的示例文件(至少不在书里介绍),而是把所有的代码片段放到 HelloFlask 文档,以代码片段的形式组织。读者需要的话可以自己复制代码到本地运行。作为示例,第一章的代码片段可以在这里看到:https://docs.helloflask.com/book/4/snippets/c1/。因为前六章的代码片段变化不大,所以第一版的读者也可以使用这些文档。

P.S. HelloFlask 文档(https://docs.helloflask.com)是我最近刚创建的一个文档集合。所有和书相关的内容都会放到这里,比如勘误、代码片段、衍生文章等。后续也会在这里写一些扩展快速入门介绍、Flask 基础知识等 Flask 相关的东西。

书名

第二版因为有独立的书号,所以可以改书名。责编老师认为原书名就很好,换书名会浪费掉第一本书建立的品牌。我也基本同意这个观点。目前的考虑是主书名不变,出于营销的考虑,副书名或许可以加一个「Python」关键字,比如改成「Python Web 开发入门、进阶与原理解析」。

封面

目前这本书主页上的封面不是正式封面(我用画图软件在初版封面上随手涂了两笔 >_<)。目前的想法是把封面标题颜色换成深红色,然后换一个封面图案,不过暂时还没想好换成什么。

措辞

第一版是用 Word 写的,排版很痛苦,毕竟要在 Word 里面排版代码。第二版也要继续用 Word 写,而且更加痛苦,因为第二版的书稿是编辑老师用工具从 PDF 文件转制成 Word 的,大部分格式都变得很混乱。希望这是我最后一次用 Word 来写技术书。

对于第一版,我交的终稿和最终成书有一些内容的变化,编辑老师会修正错别字和一些病句。但是后面也发现有一些句子修改得并不合理,导致句子原意产生了变化。这次我会在出版前过一遍校正后的书稿,确保不会再出现这样的情况。

另外,编辑老师还会替换一些词汇用法,比如把「我」替换为「笔者」,这个笔者勉强能接受,但是笔者最不能接受的就是要把所有的「点击」改成「单击」。单击一个链接?这太奇怪了。今年和编辑老师再次沟通了这件事,事情终于有了变化,书中的「点击」在这一版不用再死板的按照出版社的规定修改为「单击」。新的读者不会被指导在网页上单击一个链接了。

周边开源项目

我的很多开源项目的开发都是写作驱动的,上一本书驱动我开发了那几个 Flask 示例程序和扩展,这一次同样会有一些开发计划:

  • APIFlask:补齐所有文档,发布 1.0 版本。
  • Bootstrap-Flask:添加 Bootstrap 5 支持。
  • Flask-CKEditor:添加 CKEditor 5 支持,同时集成新版本内置的 Markdown 支持。
  • Flask-WTF:添加多文件上传验证支持。
  • Sayhello:添加分页、多语言、Markdown 支持等功能(总之就是尽可能把删减的示例程序里比较有意思的功能合并到这里)。
  • Bluelog:把 Albumy 的除了图片相关的功能合并过来,会换一个名字(暂定 WeBlog),作为一个社交博客。

如果你对这本书的新版有其他建议和想法,欢迎提出。这两本新书相关的动态会发到 Twitter @helloflask 和我的公众号,欢迎关注。