分类目录归档:计算机与编程

PyCon China 2019:基于 Flask 的 Web API 开发指南(北)

Meta

成都场 2.0 版本(推荐)

上海场 1.0 版本


这是在 PyCon China 2019 上海场 9 月 21 号分会场 B 下午 1:30 开始的演讲《基于 Flask 的 REST API 开发指南》 的介绍和相关信息。

这场演讲也会参加 PyCon China 2019 成都场(10 月 26 号)。

标题

基于 Flask 的 Web API 开发指南

介绍

作为一个微框架,轻量灵活的 Flask 很适合用来开发 Web API。相对于 Django REST Framework 和 APIStar,Flask 有什么优势和缺点?为了减少工作量,我们通常会使用一些工具来辅助编写,面对 Flask-RESTful、Flask-RESTPlus、Flask-API、Webargs、Marshmallow 等扩展和工具库,我们应该如何选择?虽然我们经常使用 REST API 这个名称,但是大部分的 API 都不够 RESTful,那么什么样的 API 才能算是 REST API?在这个议题中,我们将对这几个问题逐一进行探讨,并了解如何使用 Flask 编写出功能完善的 Web API。

总结

尽管完成了两版,但是比预先计划的内容少了很多,没能完成 Flask 扩展和其他 Web API 框架的深入对比。这些估计要放到新书里了。

  • 上海站第一版花费时间:23h 31m
  • 成都站第二版花费时间:31h 51m

PyCon China 2019:Python 虚拟环境和依赖管理工具大乱斗

这是在 PyCon China 2019 上海场 9 月 21 号分会场 B 下午 4:40 开始的闪电演讲《Python 虚拟环境和依赖管理工具大乱斗》(时长限定 5 分钟以内) 的介绍和相关信息。

PyCon China 2019 上海场购票链接:https://www.bagevent.com/event/5293611(优惠码 helloflask)

Meta

标题

Python 虚拟环境和依赖管理工具大乱斗

介绍

大多数人都会在 Python 虚拟环境和依赖管理的路上经历重重困难。一开始,你使用 pip + virtualenv + requirements.txt 的工具组合,或者再加上 virtualenvwrapper;接着,更高级的新东西出现了。Pipenv 号称新一代 Python 项目环境和依赖管理工具,打算替代上面的复杂组合,但是发展并不顺利;接着,竞争者出现了,其中实力最强的 Poetry 除了可以替代 Pipenv 来管理依赖之外,甚至还可以让你不用写 setup.py。故事就这样结束了吗?当然没有,尚在草案阶段的 PEP 582 在一边虎视眈眈,试图终结这一切混乱……

总结

加上写相关研究文章的时间,一共花了 36 小时来准备这个演讲(5 分钟),整体效果还不错,后半部分时间太紧说的有点快。

另外最后几天太忙(懒),有几处幻灯片页面样式都使用了行内 CSS,暂时没时间改,就这样吧 :/

欢迎来 PyCon China 2019 听我的演讲和 Tutorial

有没有人要参加今年的 PyCon China 上海场(9/21)?欢迎一起来组团(加我微信,备注「PyCon 组团」)!我们到时可以一起见面交流,也可以做一些 Flask 相关的活动。Flask 作者 Armin Ronacher 和 Flask 维护者之一 Hsiaoming Yang(lepture)已经确认出席,到时我可以把他们俩拉来和大家一起聊天(英语不好没关系,会说 how do you do 就行)~

其实参加技术大会并不一定是要去学东西,也可以去交朋友,换工作(微软、AWS、Elastic、JetBrains 这些公司不考虑下么),找男/女朋友(没错,我们会有相亲墙,而且 PyCon 是有女生报名的,有多少暂时保密),感受技术氛围(假装会让自己变得更厉害)……

购票和优惠码

我从 PyCon 会务那里申请到三个优惠码,helloflask 可以让大会票减掉 100 块,hellopython 可以让所有 Tutorial(三小时一个的课程)打七折,hellogrey 可以让我的那个 Tutorial(T3,Python Web 开发第一课)打六折。每个优惠码各有 20 个名额。

大会购票和详细日程见 bagevent.com/event/5293

大会官网在 https://cn.pycon.org

主题演讲

在所有主题演讲中,我比较期待(和能听懂)的大概有下面这几个(主要都在分会场 B):

  • Python 的永恒之美 – Luciano Ramalho – 《流畅的 Python》作者
  • 调试是一种新的发布:慢语言的意外优势 – Armin Ronacher – Flask 作者
  • 危险的 Flask – Hsiaoming Yang(lepture) – Flask 维护者
  • 基于 Flask 的 REST API 开发指南 – 李辉 – 我
  • Django 中的 GraphQL – 李齐雨 – LeetCode
  • Django Migration Under the Hood – 赖信涛 – 蚂蚁金服

除此之外,还有好几个分会场大概 30 个演讲,包括语言特性、人工智能、微软专场、基础架构、创新领域。另外 AWS 还提供了一个 Workshop,具体日程和演讲介绍见这里

闪电演讲

闪电演讲(5 分钟一场)是今年我参与组织 PyCon China 后推动添加的新环节,我也会负责闪电演讲的主持(第一次做主持人……紧张 ><)。闪电演讲会在分会场 B(Web 专场)进行,下午 4:40 开始,是独占环节。如果你参加今年的 PyCon,一定不要忘记来这里。

  • 【Python 虚拟环境和依赖管理工具大乱斗】 – 李辉 – 我
  • 【500 行 Python 写一个渲染器】- 谭啸 – 蚂蚁金服开发工程师
  • 【基于 OwlReady2 的人机交互】- 宋从威 – 浙江工业大学之江学院讲师
  • 【Byte Code 的革命】- 赵俊德 – 西安德新软件创始人
  • 【使用 Sphinx 制作 Web 文档】- 陈照强 – 中科院上海药物所高级研究员
  • 【一键将 C/C++ 代码转换为 Python 能调用的代码】- 韦泽华 – 上海韦纳科技有限公司
  • 【闭幕:今天什么也没听懂,PyCon 就要结束了】- 李辉 – 主持人

Tutorial:Python Web 开发第一课

今年 Tutorial 的设置主要参考了 PyCon US,形式是三小时的收费实践课程(单独收费,在 9/22),由《流畅的 Python》作者的两个 Tutorial 打头阵,而微软贡献了一个免费的 Tutorial。

我的 Tutorial 是一个面向 Python 初学者的 Web 开发入门课程(T3:Python Web 开发第一课),动手实践的部分会使用 Flask,欢迎参加。另外,Flask 维护者之一 Hsiaoming Yang 也有一场面向初学者的 Tutorial(T6:从零开始打造一个 Python 开源项目),详情见下面的链接。

Tutorial 详细日程和购票:bagevent.com/event/5886

别忘了用优惠码,我的那场(T3)可以用 hellogrey 优惠码,其他的 Tutorial 可以用 hellopython 优惠码。下面是我的 Tutorial 介绍:

介绍

这是一个面向 Python 程序员的 Web 开发课程,目标听众需要对 Python 基本语法有一定的了解,但对 Web 开发的了解程度没有要求。

在这个课程里,我会将 Python Web 开发所涉及的相关概念进行一个系统的梳理和介绍,包括 HTTP 协议、前端基础知识、常用的 Python Web 框架以及其他各种工具。

这个课程还会包含一个动手编程的环节。我会从最让人头疼的开发环境搭建开始,一步一步教你如何使用 Flask 开发一个简单的 Web 程序。

在课程过后,参与者会对整个 Python Web 开发技术栈有一个全局认识,并掌握基本的 Web 开发知识,而且会对接下来的学习路径有一个清晰的了解。

流程

一、基本概念

  • Python Web 开发技术栈地图
  • HTTP 协议基础知识(请求与响应、URL 等)
  • 前端基础知识(HTML、CSS、JavaScript、AJAX 等)
  • Python 后端框架的特点和选择(Flask、Django 等)
  • 传统 Web 程序和 Web API 的对比
  • 测试、部署、持续集成等相关概念快速扫盲

二、动手编程

  • 开发环境搭建
  • 运行和调试程序
  • 编写 HTML 模板
  • 添加表单支持
  • 添加数据库支持

三、Q&A

  • 介绍常见的学习误区和建议的学习方向
  • 关于代码或其他任何相关内容的提问

内容难度:初级

目标听众

  • 想了解 Web 开发的前端、运维、测试或其他工程师
  • 想自己做网站的编程爱好者
  • Web 开发或 Python 初学者

听众要求

  • 了解 Python 基本语法
  • 有一台安装了 Python 和浏览器的电脑,并且了解命令行基本操作
  • 其他的要求我们会具体沟通

蛇蛋:Python 中隐藏的彩蛋和笑话列表

这是一个 Python 中隐藏的彩蛋和笑话列表。如果你还知道其他有趣的彩蛋和笑话,欢迎在 GitHub 上创建 PR 添加。

项目地址:https://github.com/greyli/shedan

Python 比你想象中的更有趣。

A little girl goes into a pet shop and asks for a wabbit. The shop keeper looks down at her, smiles and says:

“Would you like a lovely fluffy little white rabbit, or a cutesy wootesly little brown rabbit?”

“Actually”, says the little girl, “I don’t think my python would notice.”

—Nick Leaton, Wed, 04 Dec 1996, Python Humor

1. Hello World

>>> import __hello__
Hello World!

Note:在 Python 中打印「Hello, World!」最简单的方式。

2. Python 之禅

>>> import this

The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

Note:Python 设计哲学 & Python 代码风格指南。

3. 《Python 之禅》丢失的一行

《Python 之禅》在 PEP 20 中引入,它本应该是 20 条格言,但是最终只写了 19 条。

Note:也许只是为了说明在文件的结尾总是应该保留一个空行。

4. 一堂简单的人生课

>>> import this
...
>>> love = this
>>> this is love
True
>>> love is True
False
>>> love is False
False
>>> love is not True or False
True
>>> love is not True or False; love is love  # FML
True

Note:不是彩蛋,只是一个解释器里的玩笑。

5. 反重力漫画

>>> import antigravity

Note:这会打开这个 xkcd 漫画,漫画展示了 Python 有多么简单易学。

6. 这不是一个选择,它定义了我们是谁

>>> from __future__ import braces
  File "<stdin>", line 1
SyntaxError: not a chance

Note:这用来快速结束任何关于在 Python 中引入大括号的讨论——不可能!

7. 起源

「Python」这个名字和蛇的种类没有关系。

Note:实际的来源是 Guido van Rossum 喜欢的电视剧《Monty Python’s Flying Circus》。

8. 反差

下面是打印出《Python 之禅》的 this.py 模块的内容:

s = """Gur Mra bs Clguba, ol Gvz Crgref

Ornhgvshy vf orggre guna htyl.
Rkcyvpvg vf orggre guna vzcyvpvg.
Fvzcyr vf orggre guna pbzcyrk.
Pbzcyrk vf orggre guna pbzcyvpngrq.
Syng vf orggre guna arfgrq.
Fcnefr vf orggre guna qrafr.
Ernqnovyvgl pbhagf.
Fcrpvny pnfrf nera'g fcrpvny rabhtu gb oernx gur ehyrf.
Nygubhtu cenpgvpnyvgl orngf chevgl.
Reebef fubhyq arire cnff fvyragyl.
Hayrff rkcyvpvgyl fvyraprq.
Va gur snpr bs nzovthvgl, ershfr gur grzcgngvba gb thrff.
Gurer fubhyq or bar-- naq cersrenoyl bayl bar --boivbhf jnl gb qb vg.
Nygubhtu gung jnl znl abg or boivbhf ng svefg hayrff lbh'er Qhgpu.
Abj vf orggre guna arire.
Nygubhtu arire vf bsgra orggre guna *evtug* abj.
Vs gur vzcyrzragngvba vf uneq gb rkcynva, vg'f n onq vqrn.
Vs gur vzcyrzragngvba vf rnfl gb rkcynva, vg znl or n tbbq vqrn.
Anzrfcnprf ner bar ubaxvat terng vqrn -- yrg'f qb zber bs gubfr!"""

d = {}
for c in (65, 97):
    for i in range(26):
        d[chr(i+c)] = chr((i+13) % 26 + c)

print("".join([d.get(c, c) for c in s]))

生成《Python 之禅》的代码本身违背了自己宣扬的风格建议。它丑陋而不美丽,隐式而不直观。

Note:使用了一种叫 ROT13 的置换加密法。

9. 有没有 C/C++ 用户?

再次引用《Python 之禅》:

There should be one-- and preferably only one --obvious way to do it.

Note:在很多语言里常常有两种方法做同一件事,即 --no 和 no--。这一行包含了一个隐藏的示例。

10. 命名标识符可以非常酷

>>> from math import pi
>>> π = pi
>>> area = π * r**2

>>> résumé = 'knows Python'
>>> 'Python' in résumé
True

>>> 我的英文名 = 'Grey Li'
>>> 我的英文名
'Grey Li'

>>> import webbrowser
>>> 我的网站 = 'http://greyli.com'
>>> webbrowser.open(我的网站)

Note:Python 3 中支持使用 Unicode 字符作为变量名。尽管如此,使用非英文字符作为变量名可能不是一个好主意,不过它确实会让处理科学公式的工作变得更有意思。

11. 选一个见面地点

>>> from antigravity import geohash
>>> # Your location, a date and that date's (or most recent) DJIA opening.
>>> # 你的位置,一个日期和这个日期(或最近)对应的道琼斯工业指数。
>>> geohash(37.421542, -122.085589, b'2005-05-26-10458.68')
37.857713 -122.544543

这可以用来生成一个 GPS 定位,在基于你所在位置的一个 1 经度长和 1 纬度宽的区域里。

Note:这个函数的源码在 这里,工作原理解释可以在这个 xkcd 漫画看到,也许这就是为什么这个函数也放到了 antigravity 模块里。

12. The FLUFL – Friendly Language Uncle For Life from PEP 401 — BDFL Retirement

>>> from __future__ import barry_as_FLUFL
>>> 1 <> 2
True
>>> 1 != 2
  File "<stdin>", line 1
    1 != 2
       ^
SyntaxError: invalid syntax

在认识到 Python 3.0 的不等运算符(!=)非常糟糕,是手指疼痛导致的错误后,FLUFL(指 Uncle Barry)重新恢复钻石型操作符(<>)作为唯一的不等运算符拼写。

Note:PEP 401 是一个愚人节玩笑(从编号可以看出来)。这个 PEP 声明 Guido van Rossum 要退休了。他会获得一个新的头衔,叫做「BDEVIL」(Benevolent Dictator Emeritus Vacationing Indefinitely from the Language,去度无限期语言假期的仁慈退休独裁者),接任者将会是 Barry Warsaw(即 Uncle Barry),Uncle Barry 的官方头衔是「FLUFL」(Friendly Language Uncle For Life,终生友好语言叔叔)。

13. InPynite?

>>> infinity = float('infinity')
>>> hash(infinity)
314159
>>> hash(float('-inf'))
-314159

一个散列值是一个固定尺寸的整型,它会标识一个特定的值。仔细观察,你会发现散列值的无限大是 10^5 x π。有意思的是,在 Python3 中,hash(float('-inf')) 将会生成 -10^5 x π,而在 Python 2 中则是 -271828(即 10^5 x e)。

Note:来源

14. types.CodeType – Not for the faint of heart

如果你开始钻进 Python 的内部,你会在 help 输出看到一个关于 types.CodeType 的警告:不适合心脏脆弱者。

>>> import types
>>> help(types.CodeType)
...
Help on class code in module builtins:                                                    
                                                                                          
class code(object)                                                                        
 |  code(argcount, kwonlyargcount, nlocals, stacksize, flags, codestring,                 
 |        constants, names, varnames, filename, name, firstlineno,                        
 |        lnotab[, freevars[, cellvars]])                                                 
 |                                                                                        
 |  Create a code object.  Not for the faint of heart.                                    
 |                                                                                        
 |  Methods defined here:                                                                 
 |                                                                                                                                       
 ...

翻译自 https://github.com/OrkoHunter/python-easter-eggs 项目,原项目没有许可协议,已通过邮件申请获得翻译和发布许可。

 

前后端分离?

有很多初学者因为受各种文章的吹捧而痴迷「前后端分离」这个词,刚开始学后端 Web 框架就想知道怎么实现前后端分离。就我目前有限的经验来看,前后端分离并没有那么重要,只是另一种程序架构(开发模式)而已。和传统的后端渲染的 Web 程序相比,并没有明显的优劣之分,两种方式都各有优缺点,也各有适合的场景。

没必要过分追捧任何一种语言、框架、工具、操作系统……都了解都能用最好,工具永远没有要做的事情重要。没有能胜任所有工作的完美工具,更多时候是按需选用最合适的工具。

开始写作第二本 Flask 书

把自己的目标公之于众,有可能会因为受到监督而更容易完成目标,也有可能会让你潜意识里感觉自己好像已经完成了目标,从而让计划更难执行。我更相信前一种理论,所以决定现在公布第二本 Flask 书的写作计划。

为什么要再写一本 Flask 书?

尽管我很想早一点深入学习更多的东西,而不是局限在 Flask(或 Python) 领域,但事实是,在这一个领域就已经有太多的东西需要研究和学习……目前来说,我最想解决的就是 Web API 的编写问题。《Flask Web 开发实战》虽然在第十章介绍了 Web API 的大部分基础概念,但是只实现了一种 OAuth 认证流程,也没能深入更多内容,包括数据校验、请求封装等。因此,我决定再写一本书来覆盖这个主题。

另一个原因是,我在上一本书的电商页面、豆瓣条目还有其他地方收集到了一些批评,其中有一些很中肯,所以我想写一本更好的 Flask 书。除了克服这些批评里提到的缺点,我也会尝试更科学的写作方式,不会像上一本书那样在早期印刷版本包含那么多的笔误和疏漏。

作为试水,我在 PyCon China 2019 上海场会有一个相关主题的演讲:《基于 Flask 的 Web API 开发指南》,如果你感兴趣的话,可以考虑报名参加

新书会包含哪些内容?

不同于《Flask Web 开发实战》所追求的大而全,这本书的定位是一个小而精的 Flask 书。它会包含下面这几部分:

  • 一个更轻松简单的入门部分。
  • 进阶部分:开发一个传统 Web 程序。
  • 作为重点的 Web API 开发部分。
  • Flask 相关的进阶部分,包括缓存、异步任务、容器部署等。
  • 可能会加入的其他内容:FastAPI、Django REST Framework、GraphQL。

这本书一来可以衔接《Flask 入门教程》,二来可以补充《Flask Web 开发实战》没有覆盖的内容。当然,对于学习 Flask,囊括几乎所有相关主题的《Flask Web 开发实战》仍然是一个不错的选择。

对于相同的主题,我会考虑使用不同的工具,比如《Flask Web 开发实战》里单元测试使用 unitttest,那么这本书就会介绍用 pytest;上一本书里编辑器介绍使用 PyCharm,这一本书或许就会介绍使用 VSCode。

下面是这本书的其他具体设计:

  • 只使用一个示例程序,贯穿全书。
  • 使用中文作为示例程序的界面语言。
  • 使用 Python3,但在书中对 Python2 兼容部分添加必要的提示。
  • 对书中的代码块添加尽可能多的注释。
  • 添加一个「术语表」,收集所有 Flask 和 Web 开发相关的术语,尝试给它们下一个简单易懂的定义。
  • 添加一个「常见错误速查表」,列出常见错误、错误解释和对应的解决方法(在维护上一本书的时间里,我处理了大量提问,见识过各种错误和误区)。

作为后续,在这本书完成后,我计划写一本电子书来介绍如何使用 Vue.js 基于这本 Flask 书编写的 Web API 来开发客户端。尽管我现在还没入门 Vue.js……但是我已经把放相关内容的网站域名准备好了:HelloVuejs.com(它和 HelloFlask.com 是兄弟域名 :p)

什么时间能完成?

预计的发售时间是明年愚人节,即 2020 年 4 月 1 日。因为 Flask 的诞生时间是 2010 年的愚人节,所以明年愚人节会是 Flask 诞生十周年纪念日,一个很完美的发售时间。

如果你对这本书感兴趣,可以关注我的微信公众号Twitter豆瓣账号获取最新动态,或是访问这本书的主页

2020/4/1 更新:Flask 新书完成时间推迟

你想看到什么内容?

在公开上一本写作消息的文章里,我征集到了大约 40 条建议,虽然没能完全采纳,但我都一一考虑过这些很有价值的建议。对于这本新书,在内容、形式或是其他任何方面,你有什么意见和建议?欢迎发评论分享你的想法,谢谢。

PyCon China 2019 闪电演讲和 Flask 主题演讲征集

在 Python 诞生 30 周年之际,PyCon China 2019 将于 9 月 21 日在上海(主会场)举行,今年我们设置了闪电演讲和 Flask 专场,正在征集相关议题。

如果你对演讲不感兴趣,但是想购票参加,可以直接跳到文章结尾。

闪电演讲

5 分钟能做的事情很多,但是你有没有想过用这 5 分钟在技术大会上进行一场闪电演讲?如果你有些心动的话,现在就一个这样的机会放在你面前!

尽管只有 5 分钟,你仍然需要做足准备。反过来,5 分钟并不短,足够让你完成一场让人记忆深刻的精彩演讲。

不用担心自己「资历」不够,因为我们只关心你的演讲内容是否有趣和有质量;也不用担心自己没有演讲经验,因为我们将在会前进行一系列线上培训和交流,帮助你完成几次试讲并给出相应的建议和指导。

无论你最终是否提交议题,只要你感兴趣,都可以加我的微信(备注「闪电演讲」),我会邀请你加入微信交流群。

Flask 主题演讲

除了策划闪电演讲,我还负责策划一个 Flask 专场。如果你在 Flask 方面有过丰富的探索和实践,想和大家分享你的知识和经验,欢迎报名。当然,如果你想分享的内容和 Flask 无关,但和 Python 有关,也欢迎报名。

总而言之,只要你的演讲和 Python 有关,并且内容不算无聊,那就大胆提交你的议题吧!

报名方式和福利

点击下面的链接即可提交议题申请:

https://jinshuju.net/f/BH2z26

报名的城市会场不限上海,报名将在 8 月 10 号截止,如果你申请的是闪电演讲,记得在「演讲类型」选项里勾选对应选项。

如果你的议题成功入选,除了可以在 PyCon China 2019 大会上分享你的想法外,你还可以获得下面的福利:

  • 免费参加 PyCon China 2019 所有城市,所有主题会场;
  • 额外赠送两张对应城市的参会票,方便你带上自己的男朋友或女朋友;
  • 免费参与组委会特别策划的「Python 之夜」大聚会。

购票参会

如果你对演讲不感兴趣,那么报名参加大会也是一个不错的选择,点击下面的链接购买上海场门票(早鸟票 7 月 30 号截止):

https://www.bagevent.com/event/5293611

在这里,你将有机会交到穿着同款格子衬衫而且使用 Python 的朋友,还有机会和很多 Python 技术大牛小牛们面对面交流。我邀请到了两位 Flask 领域的重量级嘉宾,一个是 Flask 的作者 Armin Ronacher,另一个是 Flask 的维护者之一 Hsiaoming Yang(lepture)。Flask 目前的核心维护者 David Lord 虽然没能成行,但是表示可以远程支持 Sprint 活动。

如果你离上海太远,也可以考虑参与其他会场,比如北京、杭州、成都、深圳等等,具体议程可以关注 PyChina 公众号或 PyCon China 官网

PyCon China 2019,期待你的参与!

Jinja2 和 JavaScript 模板引擎语法冲突处理

Jinja2 有三种定界符语法:

  • {{ ... }} 用来标记变量;
  • {% ... %} 用来标记语句;
  • {# ... #} 用来标记注释;

如果你同时使用了 JavaScript 模板引擎,而该 JavaScript 模板引擎也使用相同的语法标记符,那就会产生冲突。一般来说,有下面三种兼容性处理方式:

1. 使用 Jinja2 的 raw 标签标记 JavaScript 模板代码

第一种方式最直观,使用 Jinja2 的 raw 标签声明原生代码块,也就是不需要进行后端渲染的代码块。使用 raw 和 endraw 标签把 JavaScript 模板部分标记出来即可,比如:

{% raw %}
<div id="app">
    {{ js_var }}
</div>
{% endraw %}

这种方式的副作用最少,尽管需要多几行代码,但不会影响你写 Jinja2 或其他 JavaScript 库的语法习惯。

2. 修改 Jinja2 的语法定界符号

第二种方式是修改 Jinja2 的语法定界符号,一般只修改变量定界符即可,其他的按需修改。具体通过修改程序实例的下面几个属性来实现:

from flask import Flask

app = Flask(__name__)

app.jinja_env.block_start_string = '(%' # 修改块开始符号
app.jinja_env.block_end_string = '%)' # 修改块结束符号
app.jinja_env.variable_start_string = '((' # 修改变量开始符号
app.jinja_env.variable_end_string = '))' # 修改变量结束符号
app.jinja_env.comment_start_string = '(#' # 修改注释开始符号
app.jinja_env.comment_end_string = '#)' # 修改注释结束符号

3. 修改 JavaScript 模板的语法定界符号

第三种方式是修改 JavaScript 模板的语法定界符号,具体方法因 JavaScript 模板/框架而异,可以参见相关文档了解。以 Vuejs 为例,下面将模板定界符改为中括号:

var app = new Vue({
  el: "#app",
  delimiters: ["[[", "]]"],
  data: {
    message: "Hello Vue!"
  }
})

折中方案

如果你觉得使用 raw 标签太麻烦,而修改语法定界符又不习惯,这里还有一个折中方法:两边都使用双花括号作为定界符,但根据花括号内部是否添加空格来进行区分。

具体来说,对 Jinja2 变量使用 Jinja2 标准语法,也就是使用 {{ 作为变量开始符号,注意花括号右侧有一个空格,结束符号类似,需要在花括号左侧加入一个空格,即 }}。实际示例如下:

{{ jinja_var }}

而 JavaScript 模板使用没有空格的双花括号,即:

 {{js_var}}

这是一种更适合心细的懒人的方法,如果是团队项目,可能会对不习惯这种用法的人造成困扰,记得在文档里注明。这种方式只需要修改 Jinja2 定界符:

app.jinja_env.variable_start_string = '{{ '
app.jinja_env.variable_end_string = ' }}'

另外需要注意的是,如果你使用了其他 Flask 扩展的内置 Jinja2 模板或宏,需要确保它们都使用了包含空格的标准 Jinja2 语法。举例来说,用来方便集成 Bootstrap 的 Flask-Bootstrap 就没法使用,需要使用替代的 Bootstrap-Flask。其他扩展,比如 Flask-Admin,Flask-Security 暂未测试,欢迎了解的同学反馈兼容情况。

在 Flask 项目中使用 Celery(with 工厂模式 or not)

本文隶属于《Flask Web 开发实战》番外系列。这篇文章会介绍如何在 Flask 项目中集成 Celery。

创建 Celery 程序

第一步是创建一个 Celery 程序实例。因为 Flask 程序实例通常会命名为 app,为了避免冲突,我们一般会把 Celery 实例命名为 celery 或 celery_app:

from celery import Celery

celery = Celery(__name__, broker='pyamqp://guest@localhost//')

@celery.task
def add(x, y):
    return x + y

组织和加载配置

大多数教程,包括目前的 Flask 文档里都会介绍用下面的方式加载配置:

celery.conf.update(app.config)  # 这里的 app 是 Flask 程序实例

也就是把 Celery 配置和 Flask 配置写在一起,然后从 Flask 程序实例的配置字典里更新配置。

但问题是,Celery 从 4.0 开始启用新的小写配置名,某些配置被新的名称替换。虽然旧的大写配置仍然支持,但如果你打算使用小写配置名,或是打算在未来进行迁移,这里的配置加载方式就会失效,因为 Flask 在从文件或对象导入配置时只会导入大写形式的配置变量。

因此,我建议将 Celery 配置写在单独的文件里,不要和 Flask 配置混在一起。按照 Celery 文档的示例,你可以在当前目录创建一个 celeryconfig.py 文件(或是其他名字)保存配置:

broker_url = 'pyamqp://'
result_backend = 'rpc://'

task_serializer = 'json'
result_serializer = 'json'
accept_content = ['json']
timezone = 'Europe/Oslo'
enable_utc = True

然后使用下面的方法加载配置(使用其他模块名,或是在其他路径时,记得修改传入的字符串):

celery.config_from_object('celeryconfig')

如果需要在创建 Celery 实例时传入 broker 和 backend 配置,可以直接写出或是从配置模块中导入:

from celeryconfig import broker_url

celery = Celery(__name__, broker=broker_url)

在 Flask 程序中初始化 Celery

你可以单独创建 Celery 程序,但我们通常会需要为它添加 Flask 程序上下文支持,因为有时候你的 Celery 任务函数会用到依赖于 Flask 程序上下文的某些变量。

下面我们为 Celery 创建了一个工厂函数,在工厂函数中创建 Celery 实例,加载配置,并实现 Flask 程序上下文支持:

from flask import Flask
from celery import Celery

from celeryconfig import broker_url


def make_celery(app):
    celery = Celery(__name__, broker=broker_url)
    celery.config_from_object('celeryconfig')

    class ContextTask(celery.Task):
        def __call__(self, *args, **kwargs):
            with app.app_context():
                return self.run(*args, **kwargs)

    celery.Task = ContextTask
    return celery

app = Flask(__name__)

celery = make_celery(app)

在定义 Celery 任务的模块里,比如 tasks.py,你可以导入这个 Celery 程序实例:

from app import celery

@celery.task
def add(x, y):
    return x + y

在使用工厂函数的 Flask 程序中初始化 Celery

当 Flask 程序也使用工厂函数创建时,我们可以全局创建 Celery 程序实例,然后在创建 Flask 程序实例的工厂函数里更新 Celery 程序配置并进行上下文设置:

from flask import Flask
from celery import Celery

from celeryconfig import broker_url
 
celery = Celery(__name__, broker=broker_url)
 

def create_app():
    app = Flask(__name__)
 
    register_celery(app)
    return app
 

def register_celery(app):
    celery.config_from_object('celeryconfig')
 
    class ContextTask(celery.Task):
        def __call__(self, *args, **kwargs):
            with app.app_context():
                return self.run(*args, **kwargs)
 
    celery.Task = ContextTask

同样直接导入 Celery 实例并创建任务:

from app import celery

@celery.task
def add(x, y):
    return x + y

这本来是一个完整的 Celery 入门教程,但因为去年的一次硬盘损坏,对应的示例程序弄丢了,暂时没有毅力重写一遍,所以这篇文章只抽取了其中和 Flask 相关的内容。

因为距离初稿写作的时间已经过去半年多,Celery 的最新版本也已经是 4.3.0,如果文中有什么疏漏,或是有更好的实现方式,欢迎评论指出。

为已存在的数据库生成 SQLAlchemy / Flask-SQLAlchemy 模型类

SQLAlchemy 基于模型类对数据库表进行操作,所以,如果你想对已存在的数据库表进行操作,就要先为它编写对应的模型类。

对于简单的数据库,比如只有几张表,没有复杂的关系,表字段也很少,你可以直接对照表模式手写模型类。

其他情况下,使用自动化工具 SQLAcodegen / Flask-SQLAcodegen 自动生成模型类定义会更加方便,根据单独使用 SQALchemy 还是使用扩展 Flask-SQLAlchemy,你可以选择阅读对应的章节。

单独使用 SQLAlchemy

首先使用 pip 安装:

$ pip install sqlacodegen

执行下面的命令将模型类输出到 models.py 文件里(将覆盖目标文件原内容):

$ sqlacodegen --outfile models.py sqlite:///database.db

这个命令的格式如下:

$ sqlacodegen --outfile <输出的文件名> <数据库连接 URI>

–outfile / -o 选项设置输出的目标文件,不给出这个选项将直接在命令行输出生成的模型类定义,比如:

$ sqlacodegen sqlite:///data.db
# coding: utf-8
from sqlalchemy import Column, DateTime, Integer, String
from sqlalchemy.ext.declarative import declarative_base


Base = declarative_base()
metadata = Base.metadata


class Message(Base):
    __tablename__ = 'message'

    id = Column(Integer, primary_key=True)
    name = Column(String(20))
    body = Column(String(200))
    timestamp = Column(DateTime, index=True)

提示 如上所示,生成的模型类定义会包含 Base 和 metadata 对象定义和相关导入语句,你或许需要进行细微的调整。

你可以使用下面的命令查看更多可用的设置选项:

$ sqlacodegen --help

提示 除了使用 SQLAcodegen,你也可以直接使用内置的 Automap 扩展生成模型类。

使用 Flask-SQLAlchemy

首先使用 pip 安装 Flask-SQLAcodegen:

$ pip install flask-sqlacodegen

执行下面的命令将模型类输出到 models.py 文件里(将覆盖目标文件原内容):

$ flask-sqlacodegen --flask --outfile models.py sqlite:///database.db

这个命令的格式如下:

$ flask-sqlacodegen --flask --outfile <输出的文件名> <数据库连接 URI>

–flask 选项设置输出 Flask-SQLAlchemy 模型类,不给出这个选项将直接输出 SQLAlchemy 原生模型类定义;–outfile 选项设置输出的目标文件,不给出这个选项将直接在命令行输出生成的模型类定义,比如:

$ flask-sqlacodegen --flask sqlite:///data.db
# coding: utf-8
from sqlalchemy import Column, DateTime, Integer, String
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

class Message(db.Model):
    __tablename__ = 'message'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(20))
    body = db.Column(db.String(200))
    timestamp = db.Column(db.DateTime, index=True)

提示 如上所示,生成的模型类定义会包含一个 db 对象定义和相关导入语句,你或许需要进行细微的调整。

你可以使用下面的命令查看更多可用的设置选项:

$ flask-sqlacodegen --help

程序员的基本常识——学习篇

这个系列是《软技能》一书的摘抄整理,详细介绍见该系列首篇文章《程序员的基本常识——职业篇》。我会提取书中的有用信息,重新组织,并补充一些想法。欢迎评论补充相关信息,或是纠正错误观点。

对于心智成长,大部分情况下学校和家庭都在帮倒忙,你通常只能靠阅读来完成自我教育。在编程世界里情况类似。你也许会在学校里学习计算机基础知识,但对于大量的编程语言、框架和工具,你通常都需要自学。自学、解决问题和正确提问是程序员必须掌握的几个「元能力」。

1. 学习怎样学习

  • 我们几乎都是下意识地倾向于学习自己感兴趣的东西。主动学习是效率更高的方式。
  • 学习知识最好的方式就是立即将其用于实践。如果关于某个主题你能获得足够的知识,你就可以发挥自己心灵深处强大的创造力和好奇心。当我们能够在一件事情上尽情发挥的时候,我们的内心就倾向于吸收更多的信息,思考更有意义的问题。这种自然的探索和好奇心能够帮助我们轻而易举的记住更多的信息。
  • 一旦你已经实践过,并积累了各式各样的问题,立刻回到书本当中,你会有强烈的冲动去消化吸收其中的内容。
  • 然后,你可以把自己学到的新知识重新应用于实践。已解决实践过程中发现的问题为目标,在向着只是前进的道路上重复这个循环。
  • 最后,你可以通过写作文章或是教给其他人来加深理解。
  • 这个过程可以归纳为「学习——实践——掌握——教授」。

以学习某个 Web 框架来说,从头到尾读完一本相关的技术书,并不会让你掌握这个框架。只有在了解基础知识后,自己动手借助书中的知识编写一个 Web 程序,并尝试尽可能多的解决实际问题,才会让你学会这个框架。

2. 十步学习法

十步学习法的基本思想是先对自己要学的东西有个基本的了解。至少了解以下三个要点:

  • 如何开始:明确你要掌握哪些前导知识,从哪些资料开始最为合适。
  • 学科范围:确定你的学习方向和大致的学习范围。在开始阶段,你不需要了解每一个细节,只需大致了解学科的轮廓。
  • 基础知识:了解最基本的知识点是哪些,知道自己学的哪 20% 就能满足 80% 的日常应用。

1~6 步:前期调研,收集资料,制定学习计划(这些步骤只做一次)

  • 第 1 步:了解全局

了解自己将要学习的主题的全局,建立一个基本的概念。

  • 第 2 步:确定范围

明确自己到底要学什么,有一个可控的学习范围,而不是试图解决太大的问题。让自己的关注点落脚到更小也更可控的范围,根据你的时间预算,尝试把一个大的主题分解为多个可控的子主题。比如,「学习 HTML」可以进一步细化为「学习 HTML 的基本语法,了解页面基本结构和常用的元素」。

  • 第 3 步:定义目标

明确对你来说,学到何种地步,达成何种目标后算是完成学习。好的成功标准应该是具体的、无二义性的。以学习 HTML 为例,「学会用 HTML 编写网页」就是坏的成功标准,而「可以使用 HTML 创建一个内容丰富、语法标准、样式美观并且支持响应式的个人主页」就是一个好的成功标准。

  • 第 4 步:寻找资源

对于学习编程来说,你可以在网上找到大量的资源(这里的资源包括书、教程、视频、代码等)。试着在 Google、YouTube、亚马逊、豆瓣读书或其他网站搜索相关关键字,然后根据描述或评论选择资源。尽量选择系统性的教程或书。对于编程语言、框架和工具来说,官方入门教程一般都不会太差。

  • 第 5 步:创建学习计划

学习计划就是你的学习路径,通常你可以根据相关主题的技术书的目录来制定学习路径。好的技术书都遵循这样的规律:打好基础,做好铺垫,然后逐个展开每一章的论述。通过对比几本同类型的书籍目录,制定专属于你的学习路径。

  • 第 6 步:筛选资源

根据你的学习计划,去掉重复、无用、过时、难度不适宜的资源。只留下 1~2 个品质最高的资源即可。

7~10 步:学习——实践——掌握——教授(循环往复)

  • 第 7 步:开始学习,浅尝辄止

获取刚好足够开始动手实践的基础知识。大多数人在学习过程中通常会犯两类错误:第一类错误是在知之不多的情况下就盲目开始,即行动太早;第二类错误是在行动之前准备过多,即行动太晚。

  • 第 8 步:动手操作,边玩边学

通过动手实践验证你在前一步骤学习的知识,并记录下新的疑问。比如,做一个简单的小项目。

  • 第 9 步:全面掌握,学以致用

根据你掌握的资源,解决你在实践中记录下的大量问题。同时深入了解各个主题。比如,阅读你要学习的语言、框架的源码。

  • 第 10 步:乐为人师,融会贯通

要想确定你确实掌握了某些知识,最好的方式就是把自己学到的知识教给别人。你可以在 Stack Overflow、论坛、IM 群组里回答问题,也可以写文章或录视频。把自己所学教给别人是查漏补缺的好办法,这同时也会增强你的表达能力和理解能力。

3. 寻找导师

  • 在你的软件开发生涯中,拥有一位导师可以说是一笔巨大的财富,因为一位优秀的导师能够让你无需亲身经历现实的重重考验就拥有丰富的经验,从而更高效的掌握某种技术。
  • 我们通常会犯这样的错误——根据他人的生活来判读其是否具有帮助我们的能力。然而,最好的老师往往深藏不露。你不应该因为某人在自己的生活中成就平平,或者看起来不过如此就对他的印象大打折扣。
  • 你可以找一位已经成功实现你想要做的事情的人,也可以找一位曾经帮别人实现了你现在想要做到的事情的人。
  • 导师未必一定得是非常厉害的专家,只比你领先一步的人也值得学习。
  • 你可以在本地的开发小组或是网上的论坛等地方寻找你的导师,也可以在你的公司或交际圈里寻找,或者通过付费向行家请教。
  • 优秀的书籍也可以做你的导师。
  • 在寻找导师之前,你必须要明确两件事,第一是你需要导师帮你解决什么问题;第二是为了能够换取导师的帮助,你能给他提供什么?

4. 乐为人师

  • 成为导师可以让你受益无穷。
  • 每个人都有教的能力:只要在某些地方快人一步,就能帮助别人。
  • 有时候你给比人做导师真正要做的就是给予关注。我们在生活中都需要别人的帮助以看到自己看不到的东西,因为当遇到涉及自身的问题和麻烦的时候,我们都会有些目光短浅。
  • 教授是学习的最佳途径之一。当你担任导师的时候,你会以全新的视角观察和思考,并面对更多有挑战的问题。在探究答案的过程中,你会愈加深入思考,发现自己的漏洞,甚至完全改变最初的想法。
  • 一旦我们试着向别人解释某件事情的运作原理或背后的原因的时候,我们在认知上的漏洞就会暴露出来。
  • 最好的教学方式就是以谦虚的视角来观察问题,以权威的口吻去诠释问题。你要明白,你教的目的是为了帮助别人,而不是为了证明自己的优越性或者寻求认可。
  • 除了写博客,录视频和演讲也是很好的教学方式。演讲时要心态谦卑,信心满满(而不是傲慢自大)。

5. 学历

  • 拥有(好的)学历并不是成功所必须的,但它限制了可以提供给你的职位数量,并且某种程度上也限制了你的晋升。
  • 学历教育可以提供一个相对完善的计算机基础教育。当然,这些知识你也可以通过自学获得,不过这需要花费大量的时间。
  • 如果你没有(好的)学历,那么就通过作品来证明你的能力。比如,你可以写博客,独立开发一个程序,在 GitHub 上创建或参与开源项目,或是在 Stack Overflow 回答问题。
  • 弥补措施是,你可以通过自考获得一个学位,大概需要花费两年的业余时间外加两千块的投入。

6. 知识短板

  • 专注于自身强项,这没什么不妥,但有时候,如果弱点得不到解决,通常会成为你的职业或生活的桎梏。
  • 准确识别短板的最佳方式之一就是看看自己在哪些工作上花费了大量的时间,或者一直进行重复性劳动。比如,学习操作系统或各类软件的快捷键就可以节省大量时间;编写宏或自动化脚本来简化各种软件或系统中的重复操作。
  • 维护一份清单,列出自己需要去研究或者不清楚的事物。

本文首发于公众号「李辉的代码厨房」。