From a8d7715eaa580021e0941784bb0324f29ba08419 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=86=E8=BE=89?= Date: Wed, 28 Oct 2020 20:48:13 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=BF=E6=8D=A2=E7=AE=80=E4=B9=A6=E5=9B=BE?= =?UTF-8?q?=E7=89=87=E5=9C=B0=E5=9D=80=E5=88=B0github?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...224\231\350\257\257\345\244\204\347\220\206.md" | 6 +++--- ...\240\357\274\232Web\350\241\250\345\215\225.md" | 4 ++-- ...253\240\357\274\232\345\210\206\351\241\265.md" | 4 ++-- ...224\250\346\210\267\351\200\232\347\237\245.md" | 2 +- ...220\216\345\217\260\344\275\234\344\270\232.md" | 4 ++-- ...7\202\271JavaScript\351\255\224\346\263\225.md" | 2 +- ...253\240\357\274\232\346\250\241\346\235\277.md" | 6 +++--- ...224\250\346\210\267\347\231\273\345\275\225.md" | 2 +- ...253\240\357\274\232\347\262\211\344\270\235.md" | 6 +++--- ...241\265\345\222\214\345\244\264\345\203\217.md" | 14 +++++++------- ...253\240\357\274\232\347\276\216\345\214\226.md" | 4 ++-- ...222\214\346\234\254\345\234\260\345\214\226.md" | 4 ++-- ...234\237\345\222\214\346\227\266\351\227\264.md" | 2 +- ...205\250\346\226\207\346\220\234\347\264\242.md" | 2 +- ...201\345\233\233\347\253\240\357\274\232Ajax.md" | 4 ++-- ...274\232\346\225\260\346\215\256\345\272\223.md" | 4 ++-- 16 files changed, 35 insertions(+), 35 deletions(-) diff --git "a/docs/\347\254\254\344\270\203\347\253\240\357\274\232\351\224\231\350\257\257\345\244\204\347\220\206.md" "b/docs/\347\254\254\344\270\203\347\253\240\357\274\232\351\224\231\350\257\257\345\244\204\347\220\206.md" index d07e86b..962251b 100644 --- "a/docs/\347\254\254\344\270\203\347\253\240\357\274\232\351\224\231\350\257\257\345\244\204\347\220\206.md" +++ "b/docs/\347\254\254\344\270\203\347\253\240\357\274\232\351\224\231\350\257\257\345\244\204\347\220\206.md" @@ -10,7 +10,7 @@ 在Flask应用中爆发错误时会发生什么? 得到答案的最好的方法就是亲身体验一下。 启动应用,并确保至少有两个用户注册,以其中一个用户身份登录,打开个人主页并单击“编辑”链接。 在个人资料编辑器中,尝试将用户名更改为已经注册的另一个用户的用户名,boom!(爆炸声) 这将带来一个可怕的“Internal Server Error”页面: -![Internal Server Error](http://upload-images.jianshu.io/upload_images/4961528-a0c4d67a757f320c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![Internal Server Error](https://camo.githubusercontent.com/6962dc833a28d415cb3779540a7400d7718a455c/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f343936313532382d613063346436376137353766333230632e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430) 如果你查看运行应用的终端会话,将看到[stack trace](http://en.wikipedia.org/wiki/Stack_trace)(堆栈跟踪)。 堆栈跟踪在调试错误时非常有用,因为它们显示堆栈中调用的顺序,一直到产生错误的行: ``` @@ -56,7 +56,7 @@ sqlite3.IntegrityError: UNIQUE constraint failed: user.username 现在让应用再次崩溃,以在浏览器中查看交互式调试器: -![Flask调试器](http://upload-images.jianshu.io/upload_images/4961528-579598dfc9072e67.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![Flask调试器](https://camo.githubusercontent.com/24a52086ae210b14d50009ebaaab68693c3a7430/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f343936313532382d353739353938646663393037326536372e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430) 该调试器允许你展开每个堆栈框来查看相应的源代码上下文。 你也可以在任意堆栈框上打开Python提示符并执行任何有效的Python表达式,例如检查变量的值。 @@ -119,7 +119,7 @@ from app import routes, models, errors ``` 如果在终端界面设置环境变量`FLASK_DEBUG=0`,然后再次出发重复用户名的BUG,你将会看到一个更加友好的错误页面。 -![自定义500错误页面](http://upload-images.jianshu.io/upload_images/4961528-80653185821b8d8c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![自定义500错误页面](https://camo.githubusercontent.com/f18aa92c972ba8edd4e2bfa681f1b8edd2d0af2c/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f343936313532382d383036353331383538323162386438632e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430) ## 通过电子邮件发送错误 diff --git "a/docs/\347\254\254\344\270\211\347\253\240\357\274\232Web\350\241\250\345\215\225.md" "b/docs/\347\254\254\344\270\211\347\253\240\357\274\232Web\350\241\250\345\215\225.md" index 75fa412..f1ce2b2 100644 --- "a/docs/\347\254\254\344\270\211\347\253\240\357\274\232Web\350\241\250\345\215\225.md" +++ "b/docs/\347\254\254\344\270\211\347\253\240\357\274\232Web\350\241\250\345\215\225.md" @@ -154,7 +154,7 @@ def login(): 此时,你可以验证结果了。运行该应用,在浏览器的地址栏中输入`http://localhost:5000/`,然后点击顶部导航栏中的“Login”链接来查看新的登录表单。 是不是非常炫酷? -![登录表单](http://upload-images.jianshu.io/upload_images/4961528-6d1753699807685a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![登录表单](https://camo.githubusercontent.com/5be5cf105f6c1de5b90c066a9b3b275100cab50f/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f343936313532382d366431373533363939383037363835612e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430) ## 接收表单数据 @@ -259,7 +259,7 @@ def login(): 如果你尝试在未填写username和password字段的情况下提交表单,就可以看到显眼的红色错误信息了。 -![表单验证](http://upload-images.jianshu.io/upload_images/4961528-93817f3c32374fd7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![表单验证](https://camo.githubusercontent.com/ff453a61b73542c4b15b5bdca99a50e722d2157b/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f343936313532382d393338313766336333323337346664372e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430) ## 生成链接 diff --git "a/docs/\347\254\254\344\271\235\347\253\240\357\274\232\345\210\206\351\241\265.md" "b/docs/\347\254\254\344\271\235\347\253\240\357\274\232\345\210\206\351\241\265.md" index c812762..392d6c7 100644 --- "a/docs/\347\254\254\344\271\235\347\253\240\357\274\232\345\210\206\351\241\265.md" +++ "b/docs/\347\254\254\344\271\235\347\253\240\357\274\232\345\210\206\351\241\265.md" @@ -179,7 +179,7 @@ def explore(): 此时,我建议你在应用上再次尝试一下这个功能,以便体验最后的用户接口的完善。 -![用户动态](http://upload-images.jianshu.io/upload_images/4961528-b777226f688b5e11.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![用户动态](https://camo.githubusercontent.com/e9ed3501f7d17d49e290109426d94d439bb8f0b1/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f343936313532382d623737373232366636383862356531312e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430) ## 用户动态的分页 @@ -305,7 +305,7 @@ def index(): 主页和发现页都添加了分页链接。第一个链接标记为“Newer posts”,并指向前一页(请记住,我显示的用户动态按时间的倒序来排序,所以第一页是最新的内容)。 第二个链接标记为“Older posts”,并指向下一页的帖子。 如果这两个链接中的任何一个都是`None`,则通过条件过滤将其从页面中省略。 -![分页](http://upload-images.jianshu.io/upload_images/4961528-d126bfd1d8dce705.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![分页](https://camo.githubusercontent.com/3f48feabac9a41940364cd7694b587a896c32165/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f343936313532382d643132366266643164386463653730352e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430) ## 个人主页中的分页 diff --git "a/docs/\347\254\254\344\272\214\345\215\201\344\270\200\347\253\240\357\274\232\347\224\250\346\210\267\351\200\232\347\237\245.md" "b/docs/\347\254\254\344\272\214\345\215\201\344\270\200\347\253\240\357\274\232\347\224\250\346\210\267\351\200\232\347\237\245.md" index 46e2dcc..630246c 100644 --- "a/docs/\347\254\254\344\272\214\345\215\201\344\270\200\347\253\240\357\274\232\347\224\250\346\210\267\351\200\232\347\237\245.md" +++ "b/docs/\347\254\254\344\272\214\345\215\201\344\270\200\347\253\240\357\274\232\347\224\250\346\210\267\351\200\232\347\237\245.md" @@ -242,7 +242,7 @@ def messages(): 在这里,我直接从模板中调用上面添加到User模型中的`new_messages()`方法,并将该数字存储在`new_messages`模板变量中。 然后,如果该变量不为零,我只需添加带有该数字的徽章到消息链接后面即可。 以下是这个页面的外观: -![Messages Badge](http://upload-images.jianshu.io/upload_images/4961528-75b909c6c9854ecb.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![Messages Badge](https://camo.githubusercontent.com/edd9fbaf1927836975e7c6c66d48875fd37f6291/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f343936313532382d373562393039633663393835346563622e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430) ## 动态消息通知徽章 diff --git "a/docs/\347\254\254\344\272\214\345\215\201\344\272\214\347\253\240\357\274\232\345\220\216\345\217\260\344\275\234\344\270\232.md" "b/docs/\347\254\254\344\272\214\345\215\201\344\272\214\347\253\240\357\274\232\345\220\216\345\217\260\344\275\234\344\270\232.md" index 107ef13..4bc2a07 100644 --- "a/docs/\347\254\254\344\272\214\345\215\201\344\272\214\347\253\240\357\274\232\345\220\216\345\217\260\344\275\234\344\270\232.md" +++ "b/docs/\347\254\254\344\272\214\345\215\201\344\272\214\347\253\240\357\274\232\345\220\216\345\217\260\344\275\234\344\270\232.md" @@ -12,7 +12,7 @@ 任务队列为后台作业提供了一个便捷的解决方案。 Worker进程独立于应用程序运行,甚至可以位于不同的系统上。 应用程序和worker之间的通信是通过*消息队列*完成的。 应用程序提交作业,然后通过与队列交互来监视其进度。 下图展示了一个典型的实现: -![Task Queue Diagram](http://upload-images.jianshu.io/upload_images/4961528-61103c48694df737.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![Task Queue Diagram](https://camo.githubusercontent.com/2be1de536e7cc8da47740fcbd3b0e6d0e95638b1/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f343936313532382d363131303363343836393464663733372e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430) Python中最流行的任务队列是[Celery](http://www.celeryproject.org/)。 这是一个相当复杂的软件包,它有很多选项并支持多个消息队列。 另一个流行的Python任务队列是[Redis Queue](http://python-rq.org/)(RQ),它牺牲了一些灵活性,比如只支持[Redis](https://redis.io/)消息队列,但作为交换,它的建立要比Celery简单得多。 @@ -524,7 +524,7 @@ def export_posts(): 为了完善这个功能,我想在后台任务运行时提醒用户任务完成的百分比进度。 在浏览Bootstrap组件选项时,我决定在导航栏的下方使用一个Alert组件。 Alert组件是向用户显示信息的带颜色的横条。 我用蓝色的Alert框来渲染闪现的消息。 现在我要添加一个绿色的Alert框来显示任务进度。 样式如下: -![Progress Alert](http://upload-images.jianshu.io/upload_images/4961528-7a3fc6383144226d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![Progress Alert](https://camo.githubusercontent.com/0951eff09f9c441fe930c924ac0d4eacabfd52ea/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f343936313532382d376133666336333833313434323236642e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430) *app/templates/base.html*:基础模板中的导出进度Alert组件。 diff --git "a/docs/\347\254\254\344\272\214\345\215\201\347\253\240\357\274\232\345\212\240\347\202\271JavaScript\351\255\224\346\263\225.md" "b/docs/\347\254\254\344\272\214\345\215\201\347\253\240\357\274\232\345\212\240\347\202\271JavaScript\351\255\224\346\263\225.md" index c159cfb..aa950c5 100644 --- "a/docs/\347\254\254\344\272\214\345\215\201\347\253\240\357\274\232\345\212\240\347\202\271JavaScript\351\255\224\346\263\225.md" +++ "b/docs/\347\254\254\344\272\214\345\215\201\347\253\240\357\274\232\345\212\240\347\202\271JavaScript\351\255\224\346\263\225.md" @@ -6,7 +6,7 @@ 社交网站的常见用户交互模式是,当你将鼠标悬停在用户的名称上时,可以在弹出窗口中显示用户的主要信息。 如果你从未注意到这一点,请访问Twitter,Facebook,LinkedIn或任何其他主要社交网站,当你看到用户名时,只需将鼠标指针放在上面几秒钟即可看到弹出窗口。 本章将致力于为Microblog实现该功能,你可以在下面看到预览效果: -![User Popup](http://upload-images.jianshu.io/upload_images/4961528-d34608ca599c0ca2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![User Popup](https://camo.githubusercontent.com/4b7a41fff6f962323aa6f87898693e5a30f6e211/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f343936313532382d643334363038636135393963306361322e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430) *本章的GitHub链接为:[Browse](https://github.com/miguelgrinberg/microblog/tree/v0.20), [Zip](https://github.com/miguelgrinberg/microblog/archive/v0.20.zip), [Diff](https://github.com/miguelgrinberg/microblog/compare/v0.19...v0.20).* diff --git "a/docs/\347\254\254\344\272\214\347\253\240\357\274\232\346\250\241\346\235\277.md" "b/docs/\347\254\254\344\272\214\347\253\240\357\274\232\346\250\241\346\235\277.md" index 0ced9ed..8cf6e9d 100644 --- "a/docs/\347\254\254\344\272\214\347\253\240\357\274\232\346\250\241\346\235\277.md" +++ "b/docs/\347\254\254\344\272\214\347\253\240\357\274\232\346\250\241\346\235\277.md" @@ -50,7 +50,7 @@ def index(): 利用上述的代码更新这个视图函数,然后再次在浏览器打开它的URL看看结果。 -![模拟用户](http://upload-images.jianshu.io/upload_images/4961528-4cbdf36f1c266281.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![模拟用户](https://camo.githubusercontent.com/4360344234d8217c454068826eec68a3b7d1aa96/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f343936313532382d346362646633366631633236363238312e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430) 如果我说这个函数返回HTML的方式并不友好的话,你可能会觉得诧异。设想一下,当这个视图函数中的用户和博客不断变化时,里面的代码将会变得多么的复杂。应用的视图函数及其关联的URL也会持续增长。如果哪天我决定更改这个应用的布局,那就不得不更新每个视图函数的HTML字符串。显然,随着应用的扩张,这种方式完全不可行。 @@ -164,7 +164,7 @@ Jinja2提供了`for`控制结构来应对这类问题: 大道至简,对吧? 玩玩这个新版本的应用程序,一定要逐步添加更多的内容到用户动态列表,看看模板如何调度以展现视图函数传入的所有用户动态。 -![模拟用户动态](http://upload-images.jianshu.io/upload_images/4961528-ed3d140cd60c7d2e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![模拟用户动态](https://camo.githubusercontent.com/7732c2f7c6276db17810c5e3ea9d797e95d078e8/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f343936313532382d656433643134306364363063376432652e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430) ## 模板的继承 @@ -206,5 +206,5 @@ Jinja2有一个模板继承特性,专门解决这个问题。从本质上来 自从基础模板*base.html*接手页面的布局之后,我就可以从*index.html*中删除所有这方面的元素,只留下内容部分。 `extends`语句用来建立了两个模板之间的继承关系,这样Jinja2才知道当要求呈现`index.html`时,需要将其嵌入到`base.html`中。 而两个模板中匹配的`block`语句和其名称`content`,让Jinja2知道如何将这两个模板合并成在一起。 现在,扩展应用程序的页面就变得极其方便了,我可以创建从同一个基础模板*base.html*继承的派生模板,这就是我让应用程序的所有页面拥有统一外观布局而不用重复编写代码的秘诀。 -![模板继承](http://upload-images.jianshu.io/upload_images/4961528-2d5ed4ead77f74ba.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![模板继承](https://camo.githubusercontent.com/fe04bb38e1b03984dbf08083b0adab641197b870/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f343936313532382d326435656434656164373766373462612e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430) diff --git "a/docs/\347\254\254\344\272\224\347\253\240\357\274\232\347\224\250\346\210\267\347\231\273\345\275\225.md" "b/docs/\347\254\254\344\272\224\347\253\240\357\274\232\347\224\250\346\210\267\347\231\273\345\275\225.md" index 7151746..a8aa16e 100644 --- "a/docs/\347\254\254\344\272\224\347\253\240\357\274\232\347\224\250\346\210\267\347\231\273\345\275\225.md" +++ "b/docs/\347\254\254\344\272\224\347\253\240\357\274\232\347\224\250\346\210\267\347\231\273\345\275\225.md" @@ -371,7 +371,7 @@ def register(): 这个视图函数的逻辑也是一目了然,我首先确保调用这个路由的用户没有登录。表单的处理方式和登录的方式一样。在`if validate_on_submit()`条件块下,完成的逻辑如下:使用获取自表单的username、email和password创建一个新用户,将其写入数据库,然后重定向到登录页面以便用户登录。 -![注册表单](http://upload-images.jianshu.io/upload_images/4961528-28b62433a93cf278.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![注册表单](https://camo.githubusercontent.com/fc06f87607fa183a14822986bbe7a0418a1bb4e0/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f343936313532382d323862363234333361393363663237382e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430) 精雕细琢之后,用户已经能够在此应用上注册帐户,并进行登录和注销。 请确保你尝试了我在注册表单中添加的所有验证功能,以便更好地了解其工作原理。 我将在未来的章节中再次更新用户认证子系统,以增加额外的功能,比如允许用户在忘记密码的情况下重置密码。 不过对于目前的应用来讲,这已经无碍于继续构建了。 diff --git "a/docs/\347\254\254\345\205\253\347\253\240\357\274\232\347\262\211\344\270\235.md" "b/docs/\347\254\254\345\205\253\347\253\240\357\274\232\347\262\211\344\270\235.md" index d5499a3..949f299 100644 --- "a/docs/\347\254\254\345\205\253\347\253\240\357\274\232\347\262\211\344\270\235.md" +++ "b/docs/\347\254\254\345\205\253\347\253\240\357\274\232\347\262\211\344\270\235.md" @@ -16,7 +16,7 @@ 我已经在[第四章](https://github.com/luhuisicnu/The-Flask-Mega-Tutorial-zh/blob/master/docs/%e7%ac%ac%e5%9b%9b%e7%ab%a0%ef%bc%9a%e6%95%b0%e6%8d%ae%e5%ba%93.md)中用过了一对多关系。这是该关系的示意图(译者注:实际表名分别为user和post): -![一对多关系](http://upload-images.jianshu.io/upload_images/4961528-789b72ec95228315.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![一对多关系](https://camo.githubusercontent.com/24953a3d07267864e9437dbbb499e310d0a57d1a/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f343936313532382d373839623732656339353232383331352e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430) 用户和用户动态通过这个关系来关联。其中,一个用户拥有*多*条用户动态,而一条用户动态属于*一*个用户(作者)。数据库在*多*的这方使用了一个*外键*以表示一对多关系。在上面的一对多关系中,外键是`post`表的`user_id`字段,这个字段将用户的每条动态都与其作者关联了起来。 @@ -30,7 +30,7 @@ 展现多对多关系需要使用额外的*关联表*。以下是数据库如何查找学生和教师的示例: -![多对多](http://upload-images.jianshu.io/upload_images/4961528-ac7e6ea64131bc16.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![多对多](https://camo.githubusercontent.com/2dfb55edbdeafd45b017b60469dc32652a3b6033/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f343936313532382d616337653665613634313331626331362e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430) 虽然起初看起来并不明显,但具有两个外键的关联表的确能够有效地回答所有多对多关系的查询。 @@ -50,7 +50,7 @@ 使用自引用多对多关系来实现粉丝机制的表结构示意图: -![多对多](http://upload-images.jianshu.io/upload_images/4961528-00282d0dec00d11c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![多对多](https://camo.githubusercontent.com/42f706333b5e7f3a082409b6f5aa07cace67d294/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f343936313532382d303032383264306465633030643131632e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430) `followers`表是关系的关联表。 此表中的外键都指向用户表中的数据行,因为它将用户关联到用户。 该表中的每个记录代表关注者和被关注者的一个关系。 像学生和老师的例子一样,像这样的设计允许数据库回答所有关于关注和被关注的问题,并且足够干净利落。 diff --git "a/docs/\347\254\254\345\205\255\347\253\240\357\274\232\344\270\252\344\272\272\344\270\273\351\241\265\345\222\214\345\244\264\345\203\217.md" "b/docs/\347\254\254\345\205\255\347\253\240\357\274\232\344\270\252\344\272\272\344\270\273\351\241\265\345\222\214\345\244\264\345\203\217.md" index 7093dcf..0b41254 100644 --- "a/docs/\347\254\254\345\205\255\347\253\240\357\274\232\344\270\252\344\272\272\344\270\273\351\241\265\345\222\214\345\244\264\345\203\217.md" +++ "b/docs/\347\254\254\345\205\255\347\253\240\357\274\232\344\270\252\344\272\272\344\270\273\351\241\265\345\222\214\345\244\264\345\203\217.md" @@ -58,7 +58,7 @@ def user(username): 这里唯一有趣的变化是用来生成链接到个人主页的`url_for()`调用。 由于个人主页视图函数接受一个动态参数,所以`url_for()`函数接收一个值作为关键字参数。 由于这是一个指向当前登录个人主页的链接,我可以使用Flask-Login的`current_user`对象来生成正确的URL。 -![个人主页](http://upload-images.jianshu.io/upload_images/4961528-9083e29278d26e8c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![个人主页](https://camo.githubusercontent.com/e6e460b14956850df68e443d1e6af33b863fc4a1/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f343936313532382d393038336532393237386432366538632e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430) 尝试点击顶部的`Profile`链接就能将你带到自己的个人主页。 此时,虽然没有链接来访问其他用户的主页,但是如果要访问这些页面,则可以在浏览器的地址栏中手动输入网址。 例如,如果你在应用中注册了名为“john”的用户,则可以通过在地址栏中键入 *http://localhost:5000/user/john* 来查看该用户的个人主页。 @@ -75,13 +75,13 @@ Gravatar服务使用起来非常简单。 要请求给定用户的图片,使 如果你想看一个实际的例子,我自己的Gravatar URL是 *https://www.gravatar.com/avatar/729e26a2a2c7ff24a71958d4aa4e5f35* 。Gravatar返回的图片如下: -![Miguel的头像](http://upload-images.jianshu.io/upload_images/4961528-38e05911255ecccf.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![Miguel的头像](https://camo.githubusercontent.com/23504f0c0b675a34fce4a05f99ee1e2b059de47b/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f343936313532382d333865303539313132353565636363662e6a70673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430) 默认情况下,返回的图像大小是80x80像素,但可以通过向URL的查询字符串添加`s`参数来请求不同大小的图片。 例如,要获得我自己128x128像素的头像,该URL是 *https://www.gravatar.com/avatar/729e26a2a2c7ff24a71958d4aa4e5f35?s=128* 。 另一个可传递给Gravatar的有趣参数是`d`,它让Gravatar为没有向服务注册头像的用户提供的随机头像。 我最喜欢的随机头像类型是“identicon”,它为每个邮箱都返回一个漂亮且不重复的几何设计图片。 如下: -![Identicon头像](http://upload-images.jianshu.io/upload_images/4961528-d09629c52869c605.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![Identicon头像](https://camo.githubusercontent.com/001fbd04e5b6ac6ef40f68255de3d825872e3143/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f343936313532382d643039363239633532383639633630352e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430) 请注意,一些Web浏览器插件(如Ghostery)会屏蔽Gravatar图像,因为它们认为Automattic(Gravatar服务的所有者)可以根据你发送的获取头像的请求来判断你正在访问的网站。 如果在浏览器中看不到头像,你在排查问题的时候可以考虑以下是否在浏览器中安装了此类插件。 @@ -149,7 +149,7 @@ class User(UserMixin, db.Model): {% endblock %} ``` -![头像](http://upload-images.jianshu.io/upload_images/4961528-a81330fb27bd9efd.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![头像](https://camo.githubusercontent.com/2d30a6e47d2018c96ccb89c4b0862750357574c8/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f343936313532382d613831333330666232376264396566642e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430) ## 使用Jinja2子模板 @@ -259,7 +259,7 @@ Flask中的`@before_request`装饰器注册在视图函数之前执行的函数 事实上,我在存储时间和在个人主页显示时间的时候,使用的都是UTC时区。 除此之外,显示的时间格式也可能不是你所预期的,因为实际上它是Python datetime对象的内部表示。 现在,我不会操心这两个问题,因为我将在后面的章节中讨论在Web应用中处理日期和时间的主题。 -![最后访问时间](http://upload-images.jianshu.io/upload_images/4961528-959a36af22b86609.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![最后访问时间](https://camo.githubusercontent.com/b977981691a34cb814ca78fcc72f1e5c4b045080/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f343936313532382d393539613336616632326238363630392e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430) ## 个人资料编辑器 @@ -328,7 +328,7 @@ def edit_profile(): 这个视图函数处理表单的方式和其他的视图函数略有不同。如果`validate_on_submit()`返回`True`,我将表单中的数据复制到用户对象中,然后将对象写入数据库。但是当`validate_on_submit()`返回`False`时,可能是由于两个不同的原因。这可能是因为浏览器刚刚发送了一个`GET`请求,我需要通过提供表单模板的初始版本来响应。也可能是这种情况,浏览器发送带有表单数据的`POST`请求,但该数据中的某些内容无效。对于该表单,我需要区别对待这两种情况。当第一次请求表单时,我用存储在数据库中的数据预填充字段,所以我需要做与提交相反的事情,那就是将存储在用户字段中的数据移动到表单中,这将确保这些表单字段具有用户的当前数据。但在验证错误的情况下,我不想写任何表单字段,因为它们已经由WTForms填充了。为了区分这两种情况,我需要检查`request.method`,如果它是`GET`,这是初始请求的情况,如果是`POST`则是提交表单验证失败的情况。 -![个人资料编辑器](http://upload-images.jianshu.io/upload_images/4961528-330719a5697b030a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![个人资料编辑器](https://camo.githubusercontent.com/3a658836566ea245198ffd97d20fdf3842ef5ddf/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f343936313532382d333330373139613536393762303330612e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430) 我将个人资料编辑页面的链接添加到个人主页,以便用户使用: ``` @@ -339,5 +339,5 @@ def edit_profile(): 请注意我巧妙使用的条件,它确保在查看自己的个人主页时出现编辑个人资料的链接,而在查看其他人的个人主页时不会出现。 -![个人主页和编辑链接](http://upload-images.jianshu.io/upload_images/4961528-24fae0cd3c674e9f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![个人主页和编辑链接](https://camo.githubusercontent.com/d225f82e06f91379fbb5be31be62e4cfdbb13d85/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f343936313532382d323466616530636433633637346539662e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430) diff --git "a/docs/\347\254\254\345\215\201\344\270\200\347\253\240\357\274\232\347\276\216\345\214\226.md" "b/docs/\347\254\254\345\215\201\344\270\200\347\253\240\357\274\232\347\276\216\345\214\226.md" index 4bfb49c..69c63af 100644 --- "a/docs/\347\254\254\345\215\201\344\270\200\347\253\240\357\274\232\347\276\216\345\214\226.md" +++ "b/docs/\347\254\254\345\215\201\344\270\200\347\253\240\357\274\232\347\276\216\345\214\226.md" @@ -190,7 +190,7 @@ Flask-Bootstrap在渲染表单这方面做得非常出色。 Flask-Bootstrap不 下面你可以对照几张美化前后的图片来观察转变情况。 请记住,这种转变是在不改变一行应用逻辑代码的情况下实现的! -![登录](http://upload-images.jianshu.io/upload_images/4961528-0088ea58ad5b981f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![登录](https://camo.githubusercontent.com/2228b57bd36f2eaf5f047e44e28b34f32058aa1d/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f343936313532382d303038386561353861643562393831662e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430) -![主页](http://upload-images.jianshu.io/upload_images/4961528-4c0241c56f0d2036.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![主页](https://camo.githubusercontent.com/56c989f5210294f76dd631b74b79726d60ebd5cf/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f343936313532382d346330323431633536663064323033362e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430) diff --git "a/docs/\347\254\254\345\215\201\344\270\211\347\253\240\357\274\232\345\233\275\351\231\205\345\214\226\345\222\214\346\234\254\345\234\260\345\214\226.md" "b/docs/\347\254\254\345\215\201\344\270\211\347\253\240\357\274\232\345\233\275\351\231\205\345\214\226\345\222\214\346\234\254\345\234\260\345\214\226.md" index 9bed6f4..42fecd5 100644 --- "a/docs/\347\254\254\345\215\201\344\270\211\347\253\240\357\274\232\345\233\275\351\231\205\345\214\226\345\222\214\346\234\254\345\234\260\345\214\226.md" +++ "b/docs/\347\254\254\345\215\201\344\270\211\347\253\240\357\274\232\345\233\275\351\231\205\345\214\226\345\222\214\346\234\254\345\234\260\345\214\226.md" @@ -255,7 +255,7 @@ app/translations/es/LC_MESSAGES/messages.mo 在为西班牙语或任何其他添加到项目中的语言创建*messages.mo*文件之后,可以在应用中使用这些语言。 如果你想查看应用程序以西班牙语显示的方式,则可以在Web浏览器中编辑语言配置,以将西班牙语作为首选语言。 对Chrome,这是设置页面的高级部分: -![Chrome语言选项](http://upload-images.jianshu.io/upload_images/4961528-e943991eeb05c2fc..png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![Chrome语言选项](https://camo.githubusercontent.com/2d76573f0290f3dd83835a44fa550a9a8f61fb46/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f343936313532382d653934333939316565623035633266632e2e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430) 如果你不想更改浏览器设置,另一种方法是通过使`localeselector`函数始终返回一种语言来强制实现。 对西班牙语,你可以这样做: @@ -321,7 +321,7 @@ Flask-Babel的`get_locale()`函数返回一个本地语言对象,但我只想 现在所有的日期和时间都与文本使用相同的语言了。 你可以在下面看到西班牙语的外观: -![西班牙语的Microblog](http://upload-images.jianshu.io/upload_images/4961528-335f8ee973604d80..png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![西班牙语的Microblog](https://camo.githubusercontent.com/184276bb21253e48f51cce8515403602fddd8655/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f343936313532382d333335663865653937333630346438302e2e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430) 此时,除用户在用户动态或个人资料说明中提供的文本外,所有其他的文本均可翻译成其他语言。 diff --git "a/docs/\347\254\254\345\215\201\344\272\214\347\253\240\357\274\232\346\227\245\346\234\237\345\222\214\346\227\266\351\227\264.md" "b/docs/\347\254\254\345\215\201\344\272\214\347\253\240\357\274\232\346\227\245\346\234\237\345\222\214\346\227\266\351\227\264.md" index f203716..e1fbf05 100644 --- "a/docs/\347\254\254\345\215\201\344\272\214\347\253\240\357\274\232\346\227\245\346\234\237\345\222\214\346\227\266\351\227\264.md" +++ "b/docs/\347\254\254\345\215\201\344\272\214\347\253\240\357\274\232\346\227\245\346\234\237\345\222\214\346\227\266\351\227\264.md" @@ -138,5 +138,5 @@ moment('2017-09-28T21:45:23Z').calendar() 下面,你可以看到这两个时间戳在Flask-Moment和moment.js的渲染下,表现如何: -![Flask-Moment](http://upload-images.jianshu.io/upload_images/4961528-08da411f448dceb8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![Flask-Moment](https://camo.githubusercontent.com/40c1b61bb8f50b2c43342b399e2b87152c33f455/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f343936313532382d303864613431316634343864636562382e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430) diff --git "a/docs/\347\254\254\345\215\201\345\205\255\347\253\240\357\274\232\345\205\250\346\226\207\346\220\234\347\264\242.md" "b/docs/\347\254\254\345\215\201\345\205\255\347\253\240\357\274\232\345\205\250\346\226\207\346\220\234\347\264\242.md" index d06d7df..868e878 100644 --- "a/docs/\347\254\254\345\215\201\345\205\255\347\253\240\357\274\232\345\205\250\346\226\207\346\220\234\347\264\242.md" +++ "b/docs/\347\254\254\345\215\201\345\205\255\347\253\240\357\274\232\345\205\250\346\226\207\346\220\234\347\264\242.md" @@ -494,7 +494,7 @@ def search(): 如果前一个和下一个链接的渲染逻辑有点混乱,可能查看[分页组件](https://getbootstrap.com/docs/3.3/components/#pagination)的Bootstrap文档会有所帮助。 -![搜索结果](http://upload-images.jianshu.io/upload_images/4961528-76b718808aeac83a..png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![搜索结果](https://camo.githubusercontent.com/90bf46976cda62c3ba25fe1df9744c57fa72111f/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f343936313532382d373662373138383038616561633833612e2e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430) 感想如何? 本章的内容有些激进,因为里面介绍了一些相当先进的技术。 本章中的一些概念可能需要你花一些时间才能有所领悟。本章最重要的一点是,如果你想使用与Elasticsearch不同的搜索引擎,只需要重写*app/search.py*即可。 通过这项工作的另一个重要好处是,如果我需要为另外的数据库模型添加搜索支持,我可以简单地通过向它添加`SearchableMixin`类,为`__searchable__`属性填写要索引的字段列表和SQLAlchemy事件处理程序的监听即可。 我认为这些努力是值得的,因为从现在起,处理全文索引将会变得十分容易。 diff --git "a/docs/\347\254\254\345\215\201\345\233\233\347\253\240\357\274\232Ajax.md" "b/docs/\347\254\254\345\215\201\345\233\233\347\253\240\357\274\232Ajax.md" index 8bfd97a..50e0b30 100644 --- "a/docs/\347\254\254\345\215\201\345\233\233\347\253\240\357\274\232Ajax.md" +++ "b/docs/\347\254\254\345\215\201\345\233\233\347\253\240\357\274\232Ajax.md" @@ -109,7 +109,7 @@ def index(): 获得Azure帐户后,转到Azure门户并单击左上角的“New”按钮,然后键入或选择“Translator Text API”。 当你点击“Create”按钮时,将看到一个表单,并可以在其中定义一个新的翻译器资源,然后将其添加到你的帐户中。 你可以在下面看到我是如何完成表单的: -![Azure Translator](http://upload-images.jianshu.io/upload_images/4961528-4f9a52cdbf937a60..png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![Azure Translator](https://camo.githubusercontent.com/fca33b4078ed7191d77453f8b489ad5af890cd5d/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f343936313532382d346639613532636462663933376136302e2e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430) 当你再次点击“Create”按钮时,翻译器API资源将被添加到你的帐户中。几秒钟之后,你将在顶栏中收到通知,说明部署了翻译器资源。 点击通知中的“Go to resource”按钮,然后点击左侧栏上的“Keys”选项。 你现在将看到两个Key,分别标记为“Key 1”和“Key 2”。 将其中一个Key复制到剪贴板,然后将其设置到终端的环境变量中(如果使用的是Microsoft Windows,请用`set`替换`export`): @@ -305,7 +305,7 @@ promise语法允许将`$ .post()`调用的返回值“传入”回调函数作 现在实时翻译功能已经完成! 如果你在环境中设置了有效的Microsoft Translator API Key,则现在应该能够触发翻译。 假设你的浏览器设置为偏好英语,则需要使用其他语言撰写文章以查看“翻译”链接。 下面你可以看到一个例子: -![Translation](http://upload-images.jianshu.io/upload_images/4961528-1b85536f66d9c7f3..png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![Translation](https://camo.githubusercontent.com/cd4b8dc98736bab0003ef1e97574a8db28160e98/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f343936313532382d316238353533366636366439633766332e2e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430) 在本章中,我介绍了一些需要翻译成应用支持的所有语言的新文本,因此有必要更新翻译目录: diff --git "a/docs/\347\254\254\345\233\233\347\253\240\357\274\232\346\225\260\346\215\256\345\272\223.md" "b/docs/\347\254\254\345\233\233\347\253\240\357\274\232\346\225\260\346\215\256\345\272\223.md" index b2ba520..f83af99 100644 --- "a/docs/\347\254\254\345\233\233\347\253\240\357\274\232\346\225\260\346\215\256\345\272\223.md" +++ "b/docs/\347\254\254\345\233\233\347\253\240\357\274\232\346\225\260\346\215\256\345\272\223.md" @@ -75,7 +75,7 @@ from app import routes, models 就让我们从用户模型开始吧,利用 [WWW SQL Designer](http://ondras.zarovi.cz/sql/demo)工具,我画了一张图来设计用户表的各个字段(译者注:实际表名为user): -![用户表](http://upload-images.jianshu.io/upload_images/4961528-3d7f26d60dd340ad.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![用户表](https://camo.githubusercontent.com/f2105717322eb1d2126fc73218283d7cc571d7e6/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f343936313532382d336437663236643630646433343061642e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430) `id`字段通常存在于所有模型并用作*主键*。每个用户都会被数据库分配一个id值,并存储到这个字段中。大多数情况下,主键都是数据库自动赋值的,我只需要提供`id`字段作为主键即可。 @@ -175,7 +175,7 @@ INFO [alembic.runtime.migration] Running upgrade -> e517276bb1c2, users table 让我们扩展数据库来存储用户动态,以查看实际中的关系。 这是一个新表`post`的设计(译者注:实际表名分别为user和post): -![数据库关系](http://upload-images.jianshu.io/upload_images/4961528-7057ec250f40bed5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![数据库关系](https://camo.githubusercontent.com/45034bfacb8f0443c36e619e7fd5f6c6ed10c902/687474703a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f343936313532382d373035376563323530663430626564352e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f31323430) `post`表将具有必须的`id`、用户动态的`body`和`timestamp`字段。 除了这些预期的字段之外,我还添加了一个`user_id`字段,将该用户动态链接到其作者。 你已经看到所有用户都有一个唯一的`id`主键, 将用户动态链接到其作者的方法是添加对用户`id`的引用,这正是`user_id`字段所在的位置。 这个`user_id`字段被称为*外键*。 上面的数据库图显示了外键作为该字段和它引用的表的`id`字段之间的链接。 这种关系被称为*一对多*,因为“一个”用户写了“多”条动态。