有了这款神器,轻松用 Python 写个 APP

来源:机器之心
机器学习开发者想要打造一款 App 有多难?
 
事实上,你只需要会 Python 代码就可以了,剩下的工作都可以交给一个工具。近日,Streamlit 联合创始人 Adrien Treuille 撰文介绍其开发的机器学习工具开发框架——Streamlit,这是一款专为机器学习工程师创建的免费、开源 app 构建框架。这款工具可以在你写 Python 代码的时候,实时更新你的应用。目前,Streamlit 的 GitHub Star 量已经超过 3400,在 medim 上的热度更是达到了 9000+。
有了这款神器,轻松用 Python 写个 APP
Streamlit 网站:https://streamlit.io/
GitHub地址:https://github.com/streamlit/streamlit/
有了这款神器,轻松用 Python 写个 APP
用 300 行 Python 代码,编程一个可实时执行神经网络推断的语义搜索引擎。
以我的经验,每一个不平凡的机器学习项目都是用错误百出、难以维护的内部工具整合而成的。这些工具通常用 Jupyter Notebooks 和 Flask app 写成,很难部署,需要对客户端服务器架构(C/S 架构)进行推理,且无法与 Tensorflow GPU 会话等机器学习组件进行很好的整合。
我第一次看到此类工具是在卡内基梅隆大学,之后又在伯克利、Google X、Zoox 看到。这些工具最初只是小的 Jupyter notebook:传感器校准工具、仿真对比 app、激光雷达对齐 app、场景重现工具等。
当一个工具越来越重要时,项目经理会介入其中:进程和需求不断增加。这些单独的项目变成代码脚本,并逐渐发展成为冗长的「维护噩梦」……
有了这款神器,轻松用 Python 写个 APP
机器学习工程师创建 app 的流程(ad-hoc)。
而当一个工具非常关键时,我们会组建工具团队。他们熟练地写 Vue 和 React,在笔记本电脑上贴满声明式框架的贴纸。他们的设计流程是这样式的:
有了这款神器,轻松用 Python 写个 APP
工具团队构建 app 的流程(干净整洁,从零开始)。
这简直太棒了!但是所有这些工具都需要新功能,比如每周上线新功能。然而工具团队可能同时支持 10 多个项目,他们会说:「我们会在两个月内更新您的工具。」
我们返回之前自行构建工具的流程:部署 Flask app,写 HTML、CSS 和 JavaScript,尝试对从 notebook 到样式表的所有一些进行版本控制。我和在 Google X 工作的朋友 Thiago Teixeira 开始思考:如果构建工具像写 Python 脚本一样简单呢?
我们希望在没有工具团队的情况下,机器学习工程师也能构建不错的 app。这些内部工具应该像机器学习工作流程的副产品那样自然而然地出现。写此类工具感觉就像训练神经网络或者在 Jupyter 中执行点对点分析(ad-hoc analysis)!同时,我们还想保留强大 app 框架的灵活性。我们想创造出令工程师骄傲的好工具。
我们希望的 app 构建流程如下:
有了这款神器,轻松用 Python 写个 APP
Streamlit app 构建流程。
与来自 Uber、Twitter、Stitch Fix、Dropbox 等的工程师一道,我们用一年时间创造了 Streamlit,这是一个针对机器学习工程师的免费开源 app 框架。不管对于任何原型,Streamlit 的核心原则都是更简单、更纯粹。
Streamlit 的核心原则如下:
1. 拥抱 Python
Streamlit app 是完全自上而下运行的脚本,没有隐藏状态。你可以利用函数调用来处理代码。只要你会写 Python 脚本,你就可以写 Streamlit app。例如,你可以按照以下代码对屏幕执行写入操作:
import streamlit as stst.write(‘Hello, world!’)
有了这款神器,轻松用 Python 写个 APP
2. 把 widget 视作变量
Streamlit 中没有 callback!每一次交互都只是自上而下重新运行脚本。该方法使得代码非常干净:
import streamlit as stx = st.slider(‘x’)
st.write(x, ‘squared is’, x * x)
有了这款神器,轻松用 Python 写个 APP
3 行代码写成的 Streamlit 交互 app。
3. 重用数据和计算
如果要下载大量数据或执行复杂计算,怎么办?关键在于在多次运行中安全地重用信息。Streamlit 引入了 cache primitive,它像一个持续的默认不可更改的数据存储器,保障 Streamlit app 轻松安全地重用信息。例如,以下代码只从 Udacity 自动驾驶项目(https://github.com/udacity/self-driving-car)中下载一次数据,就可得到一个简单快速的 app:
有了这款神器,轻松用 Python 写个 APP
使用 st.cache,在 Streamlit 多次运行中保存数据。代码运行说明,参见:
https://gist.github.com/treuille/c633dc8bc86efaa98eb8abe76478aa81#gistcomment-3041475。
有了这款神器,轻松用 Python 写个 APP
运行以上 st.cache 示例的输出。
简而言之,Streamlit 的工作流程如下:
  1. 每次用户交互均需要从头运行全部脚本。

  2. Streamlit 根据 widget 状态为每个变量分配最新值。

  3. 缓存保证 Streamlit 重用数据和计算。

如下图所示:
有了这款神器,轻松用 Python 写个 APP
用户事件触发 Streamlit 从头开始重新运行脚本。不同运行中仅保留缓存。
感兴趣的话,你可以立刻尝试!只需运行以下行:
网页浏览器将自动打开,并转向本地 Streamlit app。如果没有出现浏览器窗口,只需点击链接。
有了这款神器,轻松用 Python 写个 APP
这些想法很简洁,但有效,使用 Streamlit 不会妨碍你创建丰富有用的 app。我在 Zoox 和 Google X 工作时,看着自动驾驶汽车项目发展成为数 G 的视觉数据,这些数据需要搜索和理解,包括在图像数据上运行模型进而对比性能。我看到的每一个自动驾驶汽车项目都有整支团队在做这方面的工具。
在 Streamlit 中构建此类工具非常简单。以下 Streamlit demo 可以对整个 Udacity 自动驾驶汽车照片数据集执行语义搜索,对人类标注的真值标签进行可视化,并在 app 内实时运行完整的神经网络(YOLO)。
有了这款神器,轻松用 Python 写个 APP
这个 300 行代码写成的 Streamlit demo 结合了语义视觉搜索和交互式神经网络推断。
整个 app 只有 300 行 Python 代码,其中大部分是机器学习代码。事实上,整个 app 里只有 23 次 Streamlit 调用。你可以试试看:
有了这款神器,轻松用 Python 写个 APP
我们与机器学习团队合作,为他们的项目而努力时,逐渐意识到这些简单的想法会带来大量重要的收益:
Streamlit app 是纯 Python 文件。你可以使用自己喜欢的编辑器和 debugger。
有了这款神器,轻松用 Python 写个 APP
我用 Streamlit 构建 app 时喜欢用 VSCode 编辑器(左)和 Chrome(右)。
纯 Python 代码可与 Git 等源码控制软件无缝对接,包括 commits、pull requests、issues 和 comment。由于 Streamlit 的底层语言是 Python,因此你可以免费利用这些协作工具的好处。
有了这款神器,轻松用 Python 写个 APP
Streamlit app 是 Python 脚本,因此你可以使用 Git 轻松执行版本控制。
Streamlit 提供即时模式的编程环境。当 Streamlit 检测出源文件变更时,只需点击 Always rerun 即可。
有了这款神器,轻松用 Python 写个 APP
点击「Always rerun」,保证实时编程。
缓存简化计算流程。一连串缓存函数自动创建出高效的计算流程!你可以尝试以下代码:
有了这款神器,轻松用 Python 写个 APP
Streamlit 中的简单计算流程。运行以上代码,参见说明:
https://gist.github.com/treuille/ac7755eb37c63a78fac7dfef89f3517e#gistcomment-3041436。
基本上,该流程涉及加载元数据到创建摘要等步骤(load_metadata → create_summary)。该脚本每次运行时,Streamlit 仅需重新计算该流程的子集即可。
有了这款神器,轻松用 Python 写个 APP
为了保证 app 的可执行性,Streamlit 仅计算更新 UI 所必需的部分。
Streamlit 适用于 GPU。Streamlit 可以直接访问机器级原语(如 TensorFlow、PyTorch),并对这些库进行补充。例如,以下 demo 中,Streamlit 的缓存存储了整个英伟达 PGGAN。该方法可使用户在更新左侧滑块时,app 执行近乎即时的推断。
有了这款神器,轻松用 Python 写个 APP
该 Streamlit app 使用 TL-GAN 展示了英伟达 PGGAN 的效果。
Streamlit 是免费开源库,而非私有 web app。你可以本地部署 Streamlit app,不用提前联系我们。你甚至可以在不联网的情况下在笔记本电脑上本地运行 Streamlit。此外,现有项目也可以渐进地使用 Streamlit。
有了这款神器,轻松用 Python 写个 APP
渐进地使用 Streamlit 的几种方式。
以上只是 Streamlit 功能的冰山一角而已。它最令人兴奋的一点是,这些原语可以轻松组成复杂 app,但看起来却只是简单脚本。这就要涉及架构运作原理和功能了,本文暂不谈及。
有了这款神器,轻松用 Python 写个 APP
Streamlit 组件图示。
我们很高兴与社区分享 Streamlit,希望它能够帮助大家轻松将 Python 脚本转化为美观实用的机器学习 app。
原文链接:
https://towardsdatascience.com/coding-ml-tools-like-you-code-ml-models-ddba3357eace

– EOF – 

转自:https://mp.weixin.qq.com/s/tNWTzk5WkV0ZRNa3zeph-w

详尽实用的 PyCharm 教程,这篇文章值得一看

选自RealPython,作者:Jahongir Rahmonov,参与:魔王

机器之心编译

PyCharm 是一种 Python IDE,可以帮助程序员节约时间,提高生产效率。那么具体如何使用呢?本文从 PyCharm 安装到插件、外部工具、专业版功能等进行了一一介绍,希望能够帮助到大家。

 

机器之心之前也没系统地介绍过 PyCharm,怎样配置环境、怎样 DeBug、怎样同步 GitHub 等等可能都是通过经验或者摸索学会的。在本文中,我们并不会提供非常完善的指南,但是会介绍 PyCharm 最主要的一些能力,了解这些后,后面就需要我们在实践中再具体学习了。

 

机器之心的读者应该非常了解 JetBrains 开发的 PyCharm 了,它差不多是 Python 最常用的 IDE。PyCharm 可以为我们节省大量时间,它能够管理代码,并完成大量其他任务,如 debug 和可视化等。

 

本文将介绍:

 

  • PyCharm 安装

  • 在 PyCharm 中写代码

  • 在 PyCharm 中运行代码

  • 在 PyCharm 中进行代码 debug 和测试

  • 在 PyCharm 中编辑已有项目

  • 在 PyCharm 中搜索和导航

  • 在 PyCharm 中使用版本控制

  • 在 PyCharm 中使用插件和外部工具

  • 使用 PyCharm Professional 功能,如 Django 支持和科学模式

本文假设读者熟悉 Python 开发,且计算机中已安装某个版本的 Python。该教程将使用 Python 3.6 版本,屏幕截图和 demo 均来自 macOS 系统。由于 PyCharm 可在所有主流平台上运行,读者在其他系统中会看到略微不同的 UI 元素,可能需要调整某些命令。

 

PyCharm 安装

 

本文将使用 PyCharm Community Edition 2019.1 版本,该版本免费且可在所有主流平台上使用。只有最后一部分「PyCharm Professional 功能」使用的是 PyCharm Professional Edition 2019.1 版本。

 

推荐使用 JetBrains Toolbox App 安装 PyCharm。使用该 App,你可以安装不同的 JetBrains 产品或者同一产品的不同版本,并在必要的情况下更新、回滚和轻松删除任意工具。你还可以在恰当的 IDE 及版本中快速打开任意项目。

 

Toolbox App 安装指南,参见 JetBrains 官方文档:https://www.jetbrains.com/help/pycharm/installation-guide.html#toolbox。
该 App 会根据你的操作系统提供合适的安装说明。如果它无法无法准确识别系统,你可以在右上角的下拉列表中找到合适的系统。
详尽实用的 PyCharm 教程,这篇文章值得一看
安装成功后,启动该 app 并接受用户协议。在 Tools 选项下,你可以看到一个可用产品列表。从中找到 PyCharm Community,并点击 Install。
好啦,现在你的机器上已经安装 PyCharm 了。如果不想使用 Toolbox app,你可以单独安装 PyCharm。

 

启动 PyCharm,你将看到导入设置弹窗。PyCharm 会自动检测出这是首次安装,并为你选择「Do not import settings」选项。点击 OK,之后 PyCharm 会让你选择键盘映射(keymap scheme)。保留默认设置,点击右下角的「Next: UI Themes」:
详尽实用的 PyCharm 教程,这篇文章值得一看

 

PyCharm 将询问选择深色模式 Darcula 还是浅色模式。你可以选择自己喜欢的模式,并点击「Next: Launcher Script」:

详尽实用的 PyCharm 教程,这篇文章值得一看

本教程将使用深色模式 Darcula。

 

在下一个页面上,直接保留默认设置,并点击「Next: Featured plugins」,这时 PyCharm 将展示可用插件列表。点击「Start using PyCharm」,现在你可以写代码了!

 

在 PyCharm 中写代码

 

在 PyCharm 中,你可以在「项目」中执行任意操作。因此,首先你需要创建一个项目。

 

安装和打开 PyCharm 后,你会看到欢迎页面。点击「Create New Project」,出现「New Project」弹窗:
详尽实用的 PyCharm 教程,这篇文章值得一看
指定项目位置,打开 Project Interpreter 列表,选择创建新的项目解释器或者使用已有的解释器。选择「New environment using」,打开其右方的下拉列表,选择 Virtualenv、Pipenv 或 Conda。这些工具可以为不同项目单独创建 Python 环境,从而分别保存不同项目所需的依赖项。

 

你可以选择其中任意一个,本教程使用的是 Virtualenv。选择后,指定环境位置,从 Python 解释器列表中选择要安装在系统中的 base interpreter。通常,保持默认设置即可。下面有两个可选框:在新环境中继承全局包环境、令当前环境可以用其它所有项目,两个都不要选。

 

点击右下角的「Create」,创建新项目:
详尽实用的 PyCharm 教程,这篇文章值得一看
屏幕上出现「Tip of the Day」弹窗,在每次启动时 PyCharm 通过该弹窗提供 trick。关掉该弹窗。

 

现在我们可以开始新的 Python 程序了。如果你使用的是 Mac 系统,使用 Cmd+N 键;如果你使用的是 Windows 或 Linux 系统,使用 Alt+Ins 键。然后选择 Python File。你也可以在菜单中选择 File → New。将新文件命名为 guess_game.py 并点击 OK。你将看到如下 PyCharm 窗口:
详尽实用的 PyCharm 教程,这篇文章值得一看
至于测试代码,我们来快速写一个简单的猜谜游戏,即程序选择一个数字让用户来猜,在每一次猜测时,程序将告诉用户他猜的数字比神秘数字大还是小,用户猜中数字时游戏结束。以下是该游戏的代码:
详尽实用的 PyCharm 教程,这篇文章值得一看
直接键入上述代码,而不是复制粘贴。你会看到如下画面:

详尽实用的 PyCharm 教程,这篇文章值得一看

如上图所示,PyCharm 提供 Intelligent Coding Assistance 功能,可以执行代码补全、代码检查、错误高亮显示和快速修复建议。比如键入 main 并点击 tab 键,PyCharm 会自动补全整个 main 从句。

 

此外,如果你在条件句前忘记键入 if,在该句子最后增添.if 并点击 Tab 键,PyCharm 将修复该 if 条件句。该用法同样适用于 True.while。这即是 PyCharm 的 Postfix Completion 功能,它可以帮助用户减少退格键使用次数。

 

在 PyCharm 中运行代码

 

现在你已经编码完成该游戏,可以运行了。

 

该游戏程序有三种运行方式:

 

  1. 在 Mac 系统中使用快捷键 Ctrl+Shift+R,在 Windows 或 Linux 系统中,使用快捷键 Ctrl+Shift+F10。

  2. 右键单击背景,从菜单中选择「Run 『guess_game』」。

  3. 由于该程序具备__main__ 从句,你可以点击__main__ 从句左侧的绿色小箭头,选择「Run 『guess_game』」。

使用以上任一方式运行该程序,窗口底部会出现终端面板(Terminal pane),显示你的代码输出结果:

详尽实用的 PyCharm 教程,这篇文章值得一看

你可以玩一下这个游戏,看看自己能否猜中数字。(专业建议:从 50 开始猜。

 

在 PyCharm 中进行代码 debug

 

找到神秘数字了吗?如果找到了,你可能会看到一些奇怪的东西:程序没有打印出祝贺信息和显示退出按钮,而是重新开始了。这就是 bug 所在。要想发现程序重新开始的原因,你需要 debug。

 

首先,点击第 8 行代码左侧的空白区域,设置断点:
详尽实用的 PyCharm 教程,这篇文章值得一看
断点即程序运行到这一行时会自动停止,你可以探索断点处之后的代码有什么错误。接下来,从以下三种方式中选择一种开始 debug:

 

  1. 在 Mac 系统中使用 Ctrl+Shift+D 键,在 Windows 或 Linux 系统中使用 Shift+Alt+F9 键。

  2. 右键单击背景,选择「Debug 『guess_game』」。

  3. 点击__main__从句左侧的绿色小箭头,选择「Debug 『guess_game』」。

之后,你将看到底部出现 Debug 窗口:
 
详尽实用的 PyCharm 教程,这篇文章值得一看
按照下列步骤执行程序 debug:

 

  1. 注意当前行被蓝色高亮显示。

  2. Debug 窗口显示 random_int 及其值。记录该数字。(上图中该数字为 85。

  3. 点击 F8 执行当前代码行,并执行到下一行代码。如有必要,你也可以使用 F7 跳转到当前行内的函数。随着你继续执行语句,变量的变化将自动呈现在 Debugger 窗口。

  4. 注意 Debugger 标签右侧有一个 Console 标签。Console 标签和 Debugger 标签相互独立。你可以在 Console 中与程序进行交互,在 Debugger 中执行 debug 动作。

  5. 转向 Console 标签,进入猜测过程。

  6. 键入左侧 Debugger 标签中显示的数字,点击 Enter 键。

  7. 转回 Debugger 标签。

  8. 再次点击 F8,计算 if 语句。注意现在你在第 14 行。为什么不是第 11 行呢?因为第 10 行的 if 语句被计算为 False。那么为什么当你键入数字后它算出来为 False 了呢?

  9. 仔细看第 10 行,注意我们在对比 user_guess 和一个错误的项。我们应该对比用户猜测的数字和 random_int,但此处我们对比的是 randint(从 random 包导入的函数)。

  10. 将 randint 更改为 random_int,按照同样的步骤重新开始 debug。你会发现,这一次到达的是第 11 行,第 10 行算出来为 True:

详尽实用的 PyCharm 教程,这篇文章值得一看

恭喜你,bug 被修复了!

 

在 PyCharm 中进行代码测试

 

不经单元测试的应用都不可靠。PyCharm 可以帮助你快速舒适地写单元测试并运行。默认情况下,unittest 被用作测试运行器,而 PyCharm 还支持其他测试框架,如 pytest、nose、doctest、tox 和 trial。例如,你可以按照以下步骤为项目选择 pytest 测试运行器:

 

  1. 打开 Settings/Preferences → Tools → Python Integrated Tools 设置对话框。

  2. 在默认测试运行器字段中选择 pytest。

  3. 点击 OK 保存该设置。

本教程的示例将使用默认测试运行器 unittest。

 

在同一个项目中,创建文件 calculator.py,并将以下 Calculator 类放入该文件:
详尽实用的 PyCharm 教程,这篇文章值得一看
PyCharm 使得为已有代码创建测试变得轻而易举。打开 calculator.py 文件,执行以下步骤中的任意一个:

 

  • 在 Mac 系统中使用 Shift+Cmd+T 键,在 Windows 或 Linux 系统中使用 Ctrl+Shift+T。

  • 右键单击该类的背景,选择「Go To and Test」。

  • 在主菜单中吗,选择 Navigate → Test。

选择「Create New Test…」,得到以下窗口:
详尽实用的 PyCharm 教程,这篇文章值得一看

 

Target directory、Test file name 和 Test class name 这三项均保留默认设置。选中上图中两种需要测试的方法并点击 OK。好了!PyCharm 自动创建文件 test_calculator.py,并在其中创建了以下 stub test:
详尽实用的 PyCharm 教程,这篇文章值得一看
使用以下方法中的任意一个运行测试:

 

  • 在 Mac 系统中使用 Ctrl+R 键,在 Windows 或 Linux 系统中使用 Shift+F10 键。

  • 右键单击背景,选择「Run 『Unittests for test_calculator.py』」。

  • 点击测试类名称左侧的绿色小箭头,选择「Run 『Unittests for test_calculator.py』」。

 

你将看到底部出现测试窗口,所有测试均失败:
详尽实用的 PyCharm 教程,这篇文章值得一看
注意,左侧是测试结果的层次结构,右侧是终端的输出。现在,将代码更改成以下代码,实现 test_add:
详尽实用的 PyCharm 教程,这篇文章值得一看

 

重新运行测试,你会看到一个测试通过了,另一个则失败。按照如下操作探索不同选项,来展示已通过测试和被忽略测试,按照字母顺序对测试进行排序,以及按照时长对测试进行排序:

详尽实用的 PyCharm 教程,这篇文章值得一看

注意,上图中的 sleep(0.1) 方法的作用是使其中一个测试变慢,以便按时长对测试进行排序。

 

在 PyCharm 中编辑已有项目

 

单文件项目非常适合作为示例,但你通常需要处理较大的项目。这部分将介绍如何使用 PyCharm 处理较大项目。

 

为了探索 PyCharm 以项目为中心的特征,你将使用 Alcazar web 框架(该框架用于学习目的)。在本地复制该 repo(地址:https://realpython.com/optins/view/alcazar-web-framework/)。

 

当你在本地已有项目时,使用以下方法中的任意一个在 PyCharm 中打开项目:

 

  • 在主菜单中点击 File → Open。

  • 在欢迎页面点击 Open。

 

之后,在计算机中找到包含该项目的文件夹,并打开。

 

如果该项目包含虚拟环境,PyCharm 将自动使用该虚拟环境,并将它作为项目解释器。

 

如果你需要配置不同的虚拟环境 virtualenv,在 Mac 上打开 Preferences,或在 Windows 或 Linux 系统中使用 Ctrl+Alt+S 打开 Settings,找到 Project: ProjectName。打开下拉列表,选择 Project Interpreter:
详尽实用的 PyCharm 教程,这篇文章值得一看
从下拉列表中选择 virtualenv。如果没有要选择的项,则点击下拉列表右方的设置按钮选择 Add…。其余步骤和创建新项目的步骤相同。

 

在 PyCharm 中搜索和导航

 

在大项目中,我们很难记住每个事物的位置,因此快速导航和搜索非常重要。PyCharm 可以提供这些功能。接下来,我们使用上一节中打开的项目,实践以下快捷键:

 

  • 在当前文件中搜索代码段:在 Mac 系统中使用 Cmd+F 键,在 Windows 或 Linux 系统中使用 Ctrl+F 键。

  • 在整个项目中搜索代码段:在 Mac 系统中使用 Cmd+Shift+F 键,在 Windows 或 Linux 系统中使用 Ctrl+Shift+F 键。

  • 搜索类:在 Mac 系统中使用 Cmd+O 键,在 Windows 或 Linux 系统中使用 Ctrl+N 键。

  • 搜索文件:在 Mac 系统中使用 Cmd+Shift+O 键,在 Windows 或 Linux 系统中使用 Ctrl+Shift+N 键。

  • 如果你不知道要搜索的是文件、类还是代码段,则搜索全部:按两次 Shift 键。

 

导航可使用以下快捷键:

 

  • 前往变量的声明:在 Mac 系统中使用 Cmd 键,在 Windows 或 Linux 系统中使用 Ctrl 键,然后单击变量。

  • 寻找类、方法或文件的用法:使用 Alt+F7 键。

  • 查看近期更改:使用 Shift+Alt+C 键,或者在主菜单中点击 View → Recent Changes。

  • 查看近期文件:在 Mac 系统中使用 Cmd+E 键,在 Windows 或 Linux 系统中使用 Ctrl+E 键,或者在主菜单中点击 View → Recent Files。

  • 多次跳转后在导航历史中前进和后退:在 Mac 系统中使用 Cmd+[ / Cmd+] 键,在 Windows 或 Linux 系统中使用 Ctrl+Alt+Left / Ctrl+Alt+Right 键。

更多细节,参见官方文档:https://www.jetbrains.com/help/pycharm/tutorial-exploring-navigation-and-search.html。

 

PyCharm 中的版本控制

 

版本控制系统(如 Git 和 Mercurial)是现代软件开发世界中最重要的工具之一。因此,IDE 必须支持版本控制。PyCharm 在这方面做得很好,它集成了大量流行的版本控制系统,如 Git(和 Github (https://github.com/))、Mercurial、Perforce 和 Subversion。

 

注:以下示例中使用的版本控制系统为 Git。

 

配置版本控制系统(VCS)

 

要想实现 VCS 集成,你需要在顶部菜单点击 VCS → VCS Operations Popup…,或者在 Mac 系统中使用 Ctrl+V 键,在 Windows 或 Linux 系统中使用 Alt+` 键。选择 Enable Version Control Integration…,你将看到以下窗口:
详尽实用的 PyCharm 教程,这篇文章值得一看
从下拉列表中选择 Git,点击 OK,这样你就为项目设置好了 VCS。(注意,如果你打开的已有项目已经具备版本控制系统,PyCharm 将会发现并自动使用该版本控制系统。

 

这时如果你前往 VCS Operations Popup…,你会发现一个不同的弹窗,它具备选项 git add、git stash、git branch、git commit、git push 等等:
详尽实用的 PyCharm 教程,这篇文章值得一看
如果你找不到所需要的选项,你可以在顶部菜单中点击 VCS,选择 Git,在这里你可以创建和查看 pull request。

 

提交和冲突处理

 

这是 PyCharm 中 VCS 集成的两大特征,我个人经常使用并且非常喜欢。假如你完成了工作,打算提交,前往 VCS → VCS Operations Popup… → Commit…,或者在 Mac 系统中使用 Cmd+K 键,在 Windows 或 Linux 系统中使用 Ctrl+K 键。你将看到如下窗口:
详尽实用的 PyCharm 教程,这篇文章值得一看
在该窗口中,你可以:

 

  • 选择要提交的文件

  • 写下提交信息

  • 在提交前执行各项检查

  • 查看更改

  • 点击右下角 Commit 按钮旁边的箭头,选择 Commit and Push…,从而一次性完成提交和 push。

是不是感觉很神奇很迅速?特别是如果你以前经常通过命令行手动执行这些任务时。

 

团队合作中会出现合并冲突(merge conflict)。当一个人对你正在处理的文件提交更改时,你们二人更改了同一行导致更改重叠,这时 VCS 无法决定选择你的更改还是队友的更改。那么你可以使用以下箭头和符号来解决这个问题:
详尽实用的 PyCharm 教程,这篇文章值得一看

 

看起来很奇怪,我们很难分辨应该删除哪些更改、保留哪些更改。不要怕,PyCharm 来了!它可以用更好、更简洁的方法解决冲突。在顶部菜单中前往 VCS,选择 Git,然后选择 Resolve conflicts…。选择存在冲突的文件,点击 Merge,出现以下窗口:
详尽实用的 PyCharm 教程,这篇文章值得一看

 

在左侧列中,你可以查看自己做的更改。在右侧列中,可以查看队友做的更改。而中间列则显示结果。存在冲突的代码行被高亮显示,你可以在它们旁边看到 X 和 >>/<<。点击箭头表示接受更改,点击 X 则表示拒绝更改。解决所有冲突后,点击 Apply 按钮:

详尽实用的 PyCharm 教程,这篇文章值得一看

在上图中,对于第一个冲突行,作者选择拒绝自己的更改,接受队友的更改。而在第二个冲突行中,作者接受了自己的更改,拒绝了队友的更改。

 

使用 PyCharm 中的 VCS 集成还可以执行很多操作。详情参见 https://www.jetbrains.com/help/pycharm/version-control-integration.html。
在 PyCharm 中使用插件和外部工具

 

在 PyCharm 中你可以找到开发所需的几乎所有功能。如果没找到,那么很可能存在一个插件,向 PyCharm 提供你需要的功能。例如,它们可以:
 
  • 添加多语言和多框架支持

  • 使用快捷键提示(shortcut hint)、文件监视器(file watcher)等提升你的生产效率

  • 利用代码练习,帮助你学习新的编程语言

例如,IdeaVim 插件向 PyCharm 添加 Vim 模拟。如果你喜欢 Vim,这个插件可以实现不错的结合。

 

Material Theme UI 插件可将 PyCharm 的外观改变为 Material Design 的外观:
详尽实用的 PyCharm 教程,这篇文章值得一看

 

Vue.js 插件使 PyCharm 支持 Vue.js 项目。Markdown 插件使得在 IDE 内可以编辑 Markdown 文件,并实时预览渲染后的 HTML。

 

在 Mac 系统上点击 Preferences → Plugins,在 Windows 或 Linux 系统中点击 Settings → Plugins,你可以在 Marketplace 标签下找到和安装所有可用插件:
详尽实用的 PyCharm 教程,这篇文章值得一看
如果仍然没找到所需插件,你甚至可以自己开发一个。

 

如果你找不到合适的插件,又不想自己开发,因为 PyPI 上有可用的包,你可以将这个包作为外部工具添加到 PyCharm。以代码分析器 Flake8 为例。

 

首先,在所选 Terminal app 中键入 pip install flake8,从而在虚拟环境中安装 Flake8。或者也可以使用 PyCharm 集成的 Terminal:
详尽实用的 PyCharm 教程,这篇文章值得一看
在 Mac 系统上点击 Preferences → Tools,在 Windows 或 Linux 系统中点击 Settings → Tools,选择 External Tools。然后点击底部 (1) 处的 + 按钮。在弹出的窗口中,输入细节并在两个窗口中点击 OK,如下图所示:
详尽实用的 PyCharm 教程,这篇文章值得一看
上图中,Program (2) 指 Flake8,你可以在虚拟环境文件夹(bin)中找到它。Arguments (3) 表示你想用 Flake8 分析的文件。Working directory 表示项目目录。

 

你可以把这里所有项的绝对路径写死,但这就意味着你无法在其他项目中使用该外部工具,只能在一个项目中针对一个文件使用该工具。

 

因此你需要使用 Macros。它是$name$格式的变量,根据语境而变化。例如,当你编辑 first.py 时,$FileName$ 为 first.py,当你编辑 second.py 时,$FileName$ 为 second.py。你可以查看它们的列表,点击 Insert Macro… 按钮将其中一个插入。此处你使用了 macros,它们的值会根据你目前处理的项目而改变,Flake8 将继续准确执行其工作。

 

要想使用它,你需要创建文件 example.py,并在其中写入以下代码:
详尽实用的 PyCharm 教程,这篇文章值得一看
上述代码故意破坏了 Flake8 的一些规则。右键单击文件背景,选择 External Tools → Flake8。Flake8 分析结果将出现在窗口底部:
详尽实用的 PyCharm 教程,这篇文章值得一看
为了使效果更好,你可以为其添加快捷键。在 Mac 系统中选择 Preferences,在 Windows 或 Linux 系统中选择 Settings。然后,点击 Keymap → External Tools → External Tools。双击 Flake8,选择 Add Keyboard Shortcut,出现以下窗口:
详尽实用的 PyCharm 教程,这篇文章值得一看
上图中,快捷键是 Ctrl+Alt+A(本教程使用该快捷键)。你可以在文本框中添加喜欢的快捷键,然后在两个窗口中点击 OK。然后,你就可以用该快捷键,在 Flake8 的帮助下分析目前在处理的文件了。

 

PyCharm Professional 功能

 

PyCharm Professional 是 PyCharm 的付费版本,具备更多开箱即用的功能和集成。这部分将概览其主要功能,以及官方文档链接(其中详细介绍了每一项功能)。记住,以下功能在 PyCharm Community 版本中均不可用。

 

Django 支持

 

Django 是最流行和最受喜爱的 Python web 框架,PyCharm 对 Django 提供广泛的支持。要确保对 Django 的支持,需要执行以下步骤:

 

  1. 在 Mac 系统中打开 Preferences,在 Windows 或 Linux 系统中打开 Settings。

  2. 选择 Languages and Frameworks。

  3. 选择 Django。

  4. 检查复选框 Enable Django support。

  5. 应用更改。

 

现在确保了对 Django 的支持,你在 PyCharm 中的 Django 开发之旅将轻松很多。具体而言在创建项目时,你会得到一个专用的 Django 项目类型。这表示,当你选择该类型时,你将拥有所有必要文件和设置。这等同于使用 django-admin startproject mysite。

 

你也可以在 PyCharm 内直接运行 manage.py 命令。目前支持的 Django 模板,包括以下一些:

 

  •  语法和错误高亮显示

  •  代码补全

  •  导航

  •  block 名称补全

  •  自定义标签和过滤器补全

  •  标签和过滤器的快速文档

  •  模板 debug 能力

除此之外,我们还可以在其他 Django 部分(如视图、URL 和模型)中执行代码补全、对 Django ORM 提供代码追踪支持(code insight support)、对 Django 模型提供模型依赖项关系图。

 

更多细节,参见官方文档:https://www.jetbrains.com/help/pycharm/django-support7.html。

 

数据库支持

 

现代数据库开发是一个复杂的任务,需要多个支持系统和工作流。这也是 JetBrains 开发独立 IDE DataGrip 的原因。DataGrip 是独立于 PyCharm 的产品,二者的应用场景和授权都不相同。

 

但幸运的是,通过 Database tools and SQL 插件(该插件默认开启),PyCharm 可以支持 DataGrip 中的所有特性。在该插件的帮助下,你可以查询、创建和管理数据库,不管数据库在本地、服务器,还是在云端。该插件支持 MySQL、PostgreSQL、Microsoft SQL Server、SQLite、MariaDB、Oracle、Apache Cassandra 等。
 
关于该插件的更多用途,请查看文档:https://www.jetbrains.com/help/pycharm/relational-databases.html。

 

线程并发可视化(Thread Concurrency Visualization)

 

Django Channels、asyncio 和近期框架(如 Starlette (https://www.starlette.io/))表明异步 Python 编程正逐渐成为趋势。异步编程具备很多好处,但很难写,也很难 debug。在此类案例中,Thread Concurrency Visualization 就是医生,帮助你全面管理多线程应用并进行优化。

 

更多细节,参见文档:https://www.jetbrains.com/help/pycharm/thread-concurrency-visualization.html。

 

Profiler

 

说到优化,profiling 是另一种代码优化方法。profiling 可以帮助你查看代码的哪一部分占用了最多的执行时间。profiler 运行的优先级如下:

 

1. vmprof
2. yappi
3. cProfile

 

如果你没有安装 vmprof 或 yappi,则运行标准 cProfile。更多细节,参见:https://www.jetbrains.com/help/pycharm/profiler.html。

 

科学模式

 

Python 不仅是通用和 web 编程语言,由于 NumPy、SciPy、scikit-learn、Matplotlib、Jupyter 等库和工具的加持,Python 成为数据科学和机器学习领域的最优工具。有了这些强大工具,你还需要一个强大的 IDE 来支持这些库所具备的绘图、分析等所有功能。
 
关于科学模式的更多详情,参见 https://www.jetbrains.com/help/pycharm/matplotlib-support.html。
 
远程开发

 

很多应用出现 bug 的一个常见原因是,开发环境和生产环境不同。尽管在大多数情况下,开发时完美复制生产环境并不现实,但力求实现完美复刻是值得追寻的目标。

 

在 PyCharm 的帮助下,你可以使用另一台计算机(如 Linux VM)上的解释器对应用进行 debug。这样,你就可以拥有与生产环境一样的解释器了,从而避免很多因开发环境和生产环境差异导致的 bug。
 
详情参见:https://www.jetbrains.com/help/pycharm/remote-debugging-with-product.html。

 

结论

 

PyCharm 是最好的 Python 开发 IDE 之一。它提供大量优势,帮助执行例行任务,从而节约大量时间。学完本教程,现在你知道如何利用 PyCharm 提高生产效率了吗?

 

原文地址:https://realpython.com/pycharm-guide/

 

 

— EOF —

 

转自:https://mp.weixin.qq.com/s/DEhDkpMOcNeq1PR5jA7TRw

Django 3.0+Redis 3.4+Celery 4.4 应用开发(附源码)


作者:大江狗

来源:Python Web与Django开发

大家好,我是猫哥。今天分享一篇文章,作者利用Django 3.0 +Redis 3.4 +Celery 4.4开发了个小应用。应用不复杂,但知识点很多,非常适合新手练手。项目需求如下:

  • 创建两个页面,一个用于创建页面,一个用于动态展示页面详情,并提供静态HMTL文件链接

  • 一个页面创建完成后,使用Celery异步执行生成静态HTML文件的任务

  • 使用redis作为Celery的Broker

  • 使用flower监控Celery异步任务执行情况

Django 3.0+Redis 3.4+Celery 4.4 应用开发(附源码)

项目完成后演示见下面动画。本项目的GitHub源码地址在最后,请耐心阅读。

Django 3.0+Redis 3.4+Celery 4.4 应用开发(附源码)

 

第零步:pip设置国内源

国内用户使用pip安装python包特别慢,这主要是应为国内连接国外网络不稳定。为加速python包的安装,首先将pip安装源设置为国内的镜像,比如阿里云提供的镜像。

linux系统修改 ~/.pip/pip.conf (没有就创建一个), 内容如下:

[global]index-url = https://mirrors.aliyun.com/pypi/simple/

windows系统直接在user目录中创建一个pip目录,如:C:Usersxxpip,新建文件pip.ini,内容如下:

[global]index-url = http://mirrors.aliyun.com/pypi/simple/

第一步:安装Django并创建项目myproject

使用pip命令安装Django.

 pip install django==3.0.4 # 安装Django,所用版本为3.0.4

使用django-admin startproject myproject创立一个名为myproject的项目

 django-admin startproject myproject

整个项目完整目录机构如下所示, 项目名为myproject, staticpage为app名。

Django 3.0+Redis 3.4+Celery 4.4 应用开发(附源码)

第二步:安装redis和项目依赖的第三方包

项目中我们需要使用redis做Celery的中间人(Broker), 所以需要先安装redis数据库。redis网上教程很多,这里就简要带过了。

  • Windows下载地址:https://github.com/MSOpenTech/redis/releases

  • Linux下安装(Ubuntu系统):$ sudo apt-get install redis-server

本项目还需要安装如下依赖包,你可以使用pip命令逐一安装。

 pip install redis==3.4.1
 pip install celery==4.4.2
 pip install eventlet # celery 4.0+版本以后不支持在windows运行,还需额外安装eventlet库

你还可以myproject目录下新建requirements.txt加入所依赖的python包及版本,然后使用pip install -r requirements.txt命令安装所有依赖。本教程所使用的django, redis和celery均为最新版本。

 django==3.0.5
 redis==3.4.1
 celery==4.4.2  
 eventlet # for windows only

第三步:Celery基本配置

  1. 修改settings.py新增celery有关的配置。celery默认也是有自己的配置文件的,名为celeryconfig.py, 但由于管理多个配置文件很麻烦,我们把celery的配置参数也写在django的配置文件里。

 # 配置celery时区,默认时UTC。
 if USE_TZ:
     timezone = TIME_ZONE
 
 # celery配置redis作为broker。redis有16个数据库,编号0~15,这里使用第1个。
 broker_url = 'redis://127.0.0.1:6379/0'
 
 # 设置存储结果的后台
 result_backend = 'redis://127.0.0.1:6379/0'
 
 # 可接受的内容格式
 accept_content = ["json"]
 # 任务序列化数据格式
 task_serializer = "json"
 # 结果序列化数据格式
 result_serializer = "json"
 
 # 可选参数:给某个任务限流
 # task_annotations = {'tasks.my_task': {'rate_limit': '10/s'}}
 
 # 可选参数:给任务设置超时时间。超时立即中止worker
 # task_time_limit = 10 * 60
 
 # 可选参数:给任务设置软超时时间,超时抛出Exception
 # task_soft_time_limit = 10 * 60
 
 # 可选参数:如果使用django_celery_beat进行定时任务
 # beat_scheduler = "django_celery_beat.schedulers:DatabaseScheduler"
 
 # 更多选项见
 # https://docs.celeryproject.org/en/stable/userguide/configuration.html
  1. settings.py同级目录下新建celery.py,添加如下内容:

 # coding:utf-8
 from __future__ import absolute_import, unicode_literals
 import os
 from celery import Celery
 
 # 指定Django默认配置文件模块
 os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
 
 # 为我们的项目myproject创建一个Celery实例。这里不指定broker容易出现错误。
 app = Celery('myproject', broker='redis://127.0.0.1:6379/0')
 
 # 这里指定从django的settings.py里读取celery配置
 app.config_from_object('django.conf:settings')
 
 # 自动从所有已注册的django app中加载任务
 app.autodiscover_tasks()
 
 # 用于测试的异步任务
 @app.task(bind=True)
 def debug_task(self):
     print('Request: {0!r}'.format(self.request))
 
 
  1. 打开settings.py同级目录下的__init__.py,添加如下内容, 确保项目启动时即加载Celery实例

 # coding:utf-8
 from __future__ import absolute_import, unicode_literals
 
 # 引入celery实例对象
 from .celery import app as celery_app
 __all__ = ('celery_app',)

网上很多django redis + celery的教程比较老了, 坑很多。比如新版原生的Celery已经支持Django了,不需要再借助什么django-celery和celery-with-redis这种第三方库了, 配置参数名也由大写变成了小写,无需再加CELERY前缀。另外当你通过app = Celery('myproject')创建Celery实例时如果不指定Broker,很容易出现[ERROR/MainProcess] consumer: Cannot connect to amqp://guest:**@127.0.0.1:5672//: [Errno 111] Connection refused这个错误。

第四步:启动redis,测试celery是否配置成功

在Django中编写和执行自己的异步任务前,一定要先测试redis和celery是否安装好并配置成功。

首先你要启动redis服务。windows进入redis所在目录,使用redis-server.exe启动redis。Linux下使用./redis-server redis.conf启动,也可修改redis.conf将daemonize设置为yes, 确保守护进程开启。

启动redis服务后,你要先运行python manage.py runserver命令启动Django服务器(无需创建任何app),然后再打开一个终端terminal窗口输入celery命令,启动worker。

 # Linux下测试
 Celery -A myproject worker -l info
 
 # Windows下测试
 Celery -A myproject worker -l info -P eventlet

如果你能看到[tasks]下所列异步任务清单如debug_task,以及最后一句celery@xxxx ready, 说明你的redis和celery都配置好了,可以开始正式工作了。

 
 -------------- celery@DESKTOP-H3IHAKQ v4.4.2 (cliffs)
 --- ***** -----
 -- ******* ---- Windows-10-10.0.18362-SP0 2020-04-24 22:02:38
 
 - *** --- * ---
 - ** ---------- [config]
 - ** ---------- .> app:         myproject:0x456d1f0
 - ** ---------- .> transport:   redis://127.0.0.1:6379/0
 - ** ---------- .> results:     redis://localhost:6379/0
 - *** --- * --- .> concurrency: 4 (eventlet)
   -- ******* ---- .> task events: OFF (enable -E to monitor tasks in this worker)
   --- ***** -----
    -------------- [queues]
                .> celery           exchange=celery(direct) key=celery
 
 
 [tasks]
  . myproject.celery.debug_task
 
 [2020-04-24 22:02:38,484: INFO/MainProcess] Connected to redis://127.0.0.1:6379/0
 [2020-04-24 22:02:38,500: INFO/MainProcess] mingle: searching for neighbors
 [2020-04-24 22:02:39,544: INFO/MainProcess] mingle: all alone
 [2020-04-24 22:02:39,572: INFO/MainProcess] pidbox: Connected to redis://127.0.0.1:6379/0.
 [2020-04-24 22:02:39,578: WARNING/MainProcess] c:usersmissenkapycharmprojectsdjango-static-html-generatorvenvlibsite-packagesceleryfixupsdjango.py:203: UserWarning: Using sett
 ings.DEBUG leads to a memory
            leak, never use this setting in production environments!
  leak, never use this setting in production environments!''')
 [2020-04-24 22:02:39,579: INFO/MainProcess] celery@DESKTOP-H3IHAKQ ready.

第五步:Django中创建新应用staticpage

cd进入myproject文件夹,使用python manage.py startapp staticpage创建一个名为staticpage的app。我们将创建一个简单的Page模型,并编写两个视图(对应两个URLs),一个用于添加页面,一个用于展示页面详情。staticpage目录下我们将要编辑或创建5个.py文件,分别是models.py, urls.py, views.py, forms.py和tasks.py,其中前4个都是标准的Django项目文件,内容如下所示。最后一个tasks.py用于存放我们自己编写的异步任务,稍后我会详细讲解。

 # staticpage/models.py
 from django.db import models
 import os
 from django.conf import settings
 
 class Page(models.Model):
     title = models.CharField(max_length=100, verbose_name="标题")
     body = models.TextField(verbose_name="正文")
 
     def __int__(self):
         return self.title
 
     # 静态文件URL地址,比如/media/html/page_8.html
     def get_static_page_url(self):
         return os.path.join(settings.MEDIA_URL, 'html', 'page_{}.html'.format(self.id))
 
 # staticpage/urls.py
 from django.urls import path, re_path
 from . import views
 
 
 urlpatterns = [
 
     # Create a page 创建页面
     path('', views.page_create, name='page_create'),
 
     # Page detail 展示页面详情。动态URL地址为/page/8/
     re_path(r'^page/(?P<pk>d+)/

page_create视图函数中你可以看到我们在一个page实例存到数据库后调用了generate_static_page函数在后台完成静态HTML页面的生成。如果我们不使用异步的化,我们要等静态HTML文件完全生成后才能跳转到页面详情页面, 这有可能要等好几秒。generate_static_page就是我们自定义的异步任务,代码如下所示。Celery可以自动发现每个Django app下的异步任务,不用担心。

 # staticpage/tasks.py
 
 import os, time
 from django.template.loader import render_to_string
 from django.conf import settings
 from celery import shared_task
 
 @shared_task
 def generate_static_page(page_id, page_title, page_body):
     # 模拟耗时任务,比如写入文件或发送邮件等操作。
     time.sleep(5)
 
     # 获取传递的参数
     page = {'title': page_title, 'body': page_body}
     context = {'page': page, }
 
     # 渲染模板,生成字符串
     content = render_to_string('staticpage/template.html', context)
 
     # 定义生成静态文件所属目录,位于media文件夹下名为html的子文件夹里。如目录不存在,则创建。
     directory = os.path.join(settings.MEDIA_ROOT, "html")
     if not os.path.exists(directory):
         os.makedirs(directory)
 
     # 拼接目标写入文件地址
     static_html_path = os.path.join(directory, 'page_{}.html'.format(page_id))
 
     # 将渲染过的字符串写入目标文件
     with open(static_html_path, 'w', encoding="utf-8") as f:
             f.write(content)
 

本例中我们生成的静态HTML文件位于media文件夹下的html子文件夹里,这样做有两个好处:

  • 与Django的静态文件存储规范保持一致:用户产生的静态文件都放在media文件下,网站本身所依赖的静态文件都放于static文件夹下。

  • 把所有产生的静态文件放在一个目录里与动态文件相分开,利于后续通过nginx部署。

本项目中还用到了3个模板,分别是base.html, detail.html和template.html。base.html和detail.html是没有任何样式的, 仅用于动态显示内容,template.html是用来生成静态文件的模板,是带样式的,这样你就可以很快区分动态页面和静态页面。由于我们后台生成静态文件至少需要5秒钟,我们在detail.html用了点javascript实现等5秒倒计时完成后显示生成的静态HTML文件地址。

3个模板均位于staticpage/templates/staticpage/文件夹下,代码如下所示:

 # base.html
 <!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <title>添加页面</title>
 </head>
 <body>
      <h2>添加页面</h2>
      <form name="myform"  method="POST" action=".">
          {% csrf_token %}
        {{ form.as_p }}
          <button type="submit">Submit</button>
      </form>
 </body>
 </html>
 
 # detail.html
 <!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <title>{{ page.title }}</title>
 </head>
 <body>
      <h2>{{ page.title }}</h2>
      <p>{{ page.body }}</p>
 
      <p>倒计时: <span id="Time">5</span></p>
      <p id="static_url" style="display:none;"> <small><a href='{{ page.get_static_page_url }}'>跳转到静态文件</a></small></p>
 
 
 <script>
  //使用匿名函数方法
  function countDown(){
  var time = document.getElementById("Time");
  var p = document.getElementById("static_url");
  //获取到id为time标签中的内容,现进行判断
  if(time.innerHTML == 0){
  //等于0时, 显示静态HTML文件URL
  p.style.display = "block";
  }else{
  time.innerHTML = time.innerHTML-1;
  }
  }
  //1000毫秒调用一次
  window.setInterval("countDown()",1000);
  </script>
 
 </body>
 </html>
 
 # template.html 生成静态文件模板
 {% load static %}
 <html lang="en">
 <head>
 <title>{% block title %}Django文档管理{% endblock %} </title>
 <meta charset="utf-8">
 <meta name="viewport" content="width=device-width, initial-scale=1">
 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
 </head>
 
 <body>
 <nav class="navbar navbar-inverse navbar-static-top bs-docs-nav">
 
   <div class="container">
     <div class="navbar-header">
         <button type="button" class="navbar-toggle" >
           <span class="icon-bar"></span>
           <span class="icon-bar"></span>
           <span class="icon-bar"></span>                        
       </button>
         <a class="navbar-brand" href="#"><strong>Django + Celery + Redis异步生成静态文件</strong></a>
      </div>
 
       <div class="collapse navbar-collapse" id="myNavbar">
        <ul class="nav navbar-nav navbar-right">
  {% if request.user.is_authenticated %}
 
           <li class="dropdown">
               <a class="dropdown-toggle btn-green" href="#"><span class="glyphicon glyphicon-user"></span> {{ request.user.username }} <span class="caret"></span></a>
             <ul class="dropdown-menu">
               <li><a  href="#">My Account</a></li>
               <li><a  href="#">Logout</a></li>
             </ul>
           </li>  
          {% else %}  
             <li class="dropdown"><a class="dropdown-toggle btn-green" href="#"><span class="glyphicon glyphicon-user"></span> Sign Up</a></li>
 <li class="dropdown"><a class="dropdown-toggle" href="#" ><span class="glyphicon glyphicon-log-in"></span> Login</a></li>
  {% endif %}
        </ul>
 
     </div>
 
   </div>
 </nav>    
 
  <!-- Page content of course! -->
 <main id="section1" class="container-fluid">
 
 <div class="container">
     <div class="row">
      <div class="col-sm-3 col-hide">
          <ul>
              <li> <a href="{% url 'page_create' %}">添加页面</a> </li>
          </ul>
      </div>
 
      <div class="col-sm-9">
           <h3>{{ page.title }}</h3>
          {{ page.body }}
      </div>
 </div>
 
 </div>
 </main>

 <script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
 <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
 
 </body>
 </html>

第六步:在Django中注册app并添加app的URLConf

 # 修改myproject/settings.py,添加如下内容
 INSTALLED_APPS = [
     'django.contrib.admin',
     'django.contrib.auth',
     'django.contrib.contenttypes',
     'django.contrib.sessions',
     'django.contrib.messages',
     'django.contrib.staticfiles',
     'staticpage',
 ]
 
 # 设置STATIC_URL和STATIC_ROOT
 STATIC_URL = '/static/'
 STATIC_ROOT = os.path.join(BASE_DIR, 'static')
 
 # 设置MEDIA_ROOT和MEDIA_URL
 MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
 MEDIA_URL = '/media/'
 
 # 修改myproject/urls.py,添加如下内容
 from django.contrib import admin
 from django.urls import path, include
 
 from django.conf import settings
 from django.conf.urls.static import static
 
 
 urlpatterns = [
     path('admin/', admin.site.urls),
     path('', include("staticpage.urls")),
 ]
 
 # Django自带服务器默认不支持静态文件,需加入这两行。
 if settings.DEBUG:
     urlpatterns = urlpatterns + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
     urlpatterns = urlpatterns + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

第七步:启动Django服务器和并安装Celery异步任务监控工具

如果一切顺利,连续使用如下命令, 即可启动Django测试服务器。打开http://127.0.0.1:8000/即可看到我们项目开头的动画啦。注意:请确保redis和celery已同时开启。

 python manage.py makemigrations
 python manage.py migrate
 python manage.py runserver

如果你要监控异步任务的运行状态(比如是否成功,是否有返回结果), 还可以安装flower这个Celery监控工具。

 pip install flower

安装好后,你有如下两种方式启动服务器。启动服务器后,打开http://localhost:5555即可查看监控情况。

 # 从terminal终端启动, proj为项目名
 $ flower -A proj --port=5555  
 # 从celery启动
 $ celery flower -A proj --address=127.0.0.1 --port=5555

 

监控异步任务还是很重要的,强烈建议安装flower。比如下图中有些任务就失败了。

Django 3.0+Redis 3.4+Celery 4.4 应用开发(附源码)

源码地址

https://github.com/shiyunbo/django-static-page-generator-celery-redis

Django 3.0+Redis 3.4+Celery 4.4 应用开发(附源码)

送书活动

Django 3.0+Redis 3.4+Celery 4.4 应用开发(附源码)

 

本书共13章,主要内容涵盖Python语法及数据分析方法。章主要介绍数据分析的概念,使读者有一个大致的印象,并简单介绍本书频繁使用的Python的5个第三方库。第2章主要做一些准备工作,手把手带读者搭建Python环境,包括Python 3.7.6的安装和pip的安装。第3章介绍Python编程基础。第4章到第7章介绍使用Python进行简单数据分析的基础库,包括NumPy、Pandas和Matplotlib库,并介绍使用正则表达式处理数据的方法。第8章到3章属于进阶内容,但也是Python数据分析的基础,结合机器学习介绍一些常见的用于数据分析的机器学习算法及常用的数学模型。

赠书规则

赠书本数本次共包邮送书 

参与方式:在Python猫读者群抽奖,仅限群友参与。后台发“交流群”,获取入群方式。
开奖时间:2021年5月20日18:00

 

 

, views.page_detail, name=’page_detail’),
 
    ]
 
 # staticpage/views.py
 from django.shortcuts import render, redirect, get_object_or_404
 from django.urls import reverse
 from .forms import PageForm
 from .models import Page
 from .tasks import generate_static_page
 
 def page_create(request):
     if request.method == ‘POST’:
         form = PageForm(request.POST)
         if form.is_valid():
             page = form.save()
             generate_static_page.delay(page.id, page.title, page.body)
             return redirect(reverse(‘page_detail’, args=[str(page.pk)]))
     else:
         form = PageForm()
 
     return render(request, ‘staticpage/base.html’, {‘form’: form})
 
 
 def page_detail(request, pk):
     page = get_object_or_404(Page, id=pk)
     return render(request, ‘staticpage/detail.html’, {‘page’: page})
 
 # staticpage/forms.py
 from django import forms
 from .models import Page
 
 
 class PageForm(forms.ModelForm):
     class Meta:
         model = Page
         exclude = ()

page_create视图函数中你可以看到我们在一个page实例存到数据库后调用了generate_static_page函数在后台完成静态HTML页面的生成。如果我们不使用异步的化,我们要等静态HTML文件完全生成后才能跳转到页面详情页面, 这有可能要等好几秒。generate_static_page就是我们自定义的异步任务,代码如下所示。Celery可以自动发现每个Django app下的异步任务,不用担心。

 

本例中我们生成的静态HTML文件位于media文件夹下的html子文件夹里,这样做有两个好处:

  • 与Django的静态文件存储规范保持一致:用户产生的静态文件都放在media文件下,网站本身所依赖的静态文件都放于static文件夹下。

  • 把所有产生的静态文件放在一个目录里与动态文件相分开,利于后续通过nginx部署。

本项目中还用到了3个模板,分别是base.html, detail.html和template.html。base.html和detail.html是没有任何样式的, 仅用于动态显示内容,template.html是用来生成静态文件的模板,是带样式的,这样你就可以很快区分动态页面和静态页面。由于我们后台生成静态文件至少需要5秒钟,我们在detail.html用了点javascript实现等5秒倒计时完成后显示生成的静态HTML文件地址。

3个模板均位于staticpage/templates/staticpage/文件夹下,代码如下所示:

 

第六步:在Django中注册app并添加app的URLConf

 

第七步:启动Django服务器和并安装Celery异步任务监控工具

如果一切顺利,连续使用如下命令, 即可启动Django测试服务器。打开http://127.0.0.1:8000/即可看到我们项目开头的动画啦。注意:请确保redis和celery已同时开启。

 

如果你要监控异步任务的运行状态(比如是否成功,是否有返回结果), 还可以安装flower这个Celery监控工具。

 

安装好后,你有如下两种方式启动服务器。启动服务器后,打开http://localhost:5555即可查看监控情况。

 

 

监控异步任务还是很重要的,强烈建议安装flower。比如下图中有些任务就失败了。

Django 3.0+Redis 3.4+Celery 4.4 应用开发(附源码)

源码地址

https://github.com/shiyunbo/django-static-page-generator-celery-redis

转自:https://mp.weixin.qq.com/s/krlB0WdHpW1GSJhl2SinKw

Shodan:互联网上最可怕的搜索引擎

Shodan 在百度百科里被给出了这么一句话介绍:Shodan 是互联网上最可怕的搜索引擎。

为什么呢?与谷歌、百度等搜索引擎爬取网页信息不同,Shodan 爬取的是互联网上所有设备的 IP 地址及其端口号。

而随着智能家电的普及,家家户户都有许多电器连接到互联网,这些设备存在被入侵的可能性,这是十分危险的。

说了这么多,给大家体验下 shodan,让你们有更切身的理解。打开 shodan.io,在搜索框输入 Hikvision-Webs :

Shodan:互联网上最可怕的搜索引擎

你会搜素到这个品牌的摄像头设备遍及全球的IP及其暴露的端口号:

Shodan:互联网上最可怕的搜索引擎

可以看到,这台机器暴露了 17、80、111、995、3128、5000、6000、20547 端口,黑客可以根据这些端口进行针对性的攻击。

不过也不需要过于担心,如果你的服务不存在漏洞,一般是无法攻入的。但有些端口号会暴露摄像头的 web 管理端,如下:

Shodan:互联网上最可怕的搜索引擎

那么黑客可能可以用暴力破解的方式,强行进入摄像头后台管理端,获取到实时的录像。

谨记这会侵犯别人的隐私权,是违法的行为,我们是遵纪守法的好公民所以知道它的原理和危害就足够。我们的目的是运用技术保护好个人隐私,如非必要不将摄像头接入互联网,一定要接入的话,不能使用容易被破解的弱口令。

Shodan Web 端非常好用,但如果我们有从 Python 搜索的需求怎么办?

没关系,Shodan 官方也提供了 Python SDK 包,下面就来讲讲这个 SDK 包的使用。

1.准备

开始之前,你要确保 Python 和 pip 已经成功安装在电脑上。

(可选1) 如果你用 Python 的目的是数据分析,可以直接安装 Anaconda,它内置了 Python 和 pip .

(可选2) 此外,推荐大家用 VSCode 编辑器,它有许多的优点

请选择以下任一种方式输入命令安装依赖
1. Windows 环境 打开 Cmd (开始-运行- CMD)。
2. MacOS 环境 打开 Terminal (command + 空格输入 Terminal)。
3. 如果你用的是 VSCode 编辑器 或 Pycharm,可以直接使用界面下方的 Terminal.

pip install shodan
2.注册账号获取API

使用 Shodan 必须注册账号,注册网址:https://account.shodan.io/register

Shodan:互联网上最可怕的搜索引擎

输入完相关信息,点击 CREATE 会跳转到个人账户页:

Shodan:互联网上最可怕的搜索引擎

此时 API Key 会显示你的API秘钥,请记录这个秘钥,后续会使用到这个秘钥去请求接口。

3.Shodan 基本调用

Shodan 本质上就是一个搜索引擎,你只需要输入搜索的关键词:

# 公众号:Python 实用宝典
# 2021-05-04
from shodan import Shodan

api = Shodan('你的API KEY')

def search_shodan(keyword):
    # 调用搜索接口
    result = api.search(keyword)

    # 显示所有IP
    for service in result['matches']:
            print(service['ip_str'])

search_shodan("Hikvision-Webs")

结果如下:

Shodan:互联网上最可怕的搜索引擎

可惜的是,普通 API 只能像这样搜索关键字,无法使用过滤条件如: Hikvision-Webs country:"US" 搜索美国境内的所有 Hikvision 网站管理端。

如果你想要使用过滤条件,Shodan 需要你升级API权限:

Shodan:互联网上最可怕的搜索引擎

挺贵的,不过还好是一次性支付,永久使用。

4. Shodan 高级使用

Shodan 的用处当然不仅仅是在黑客攻防中,它还能用于统计。如果你想要了解哪些国家的使用这款摄像头的数量最多,可以使用 Facets 特性。

# 公众号:Python 实用宝典
# 2021-05-04
from shodan import Shodan

api = Shodan('你的API KEY')
def try_facets(query):
    FACETS = [
        'org',
        'domain',
        'port',
        'asn',
        ('country', 3),
    ]

    FACET_TITLES = {
        'org': 'Top 5 Organizations',
        'domain': 'Top 5 Domains',
        'port': 'Top 5 Ports',
        'asn': 'Top 5 Autonomous Systems',
        'country': 'Top 3 Countries',
    }

    try:
        # 使用 count() 方法可以不需要升级API,且比 search 方法更快。
        result = api.count(query, facets=FACETS)

        print('Shodan Summary Information')
        print('Query: %s' % query)
        print('Total Results: %sn' % result['total'])

        # 显示每个要素的摘要
        for facet in result['facets']:
            print(FACET_TITLES[facet])

            for term in result['facets'][facet]:
                print('%s: %s' % (term['value'], term['count']))

    except Exception as e:
        print('Error: %s' % e)

try_facets("Hikvision-Webs")

得到结果如下:

Shodan:互联网上最可怕的搜索引擎

从 Top 3 Countries 中可以看到,这款摄像头使用数量排名前三的国家分别是:美国、日本和德国。

没想到吧,Shodan 居然还能用于产品分析。同样地原理,如果你把关键词改为 apache ,你可以知道目前哪些国家使用apache服务器数量最多,最普遍被使用的版本号是什么。

简而言之,Shodan 是一个非常强大的搜索引擎,它在好人手里,能被发挥出巨大的潜能。如果 Shodan 落入坏人之手的话,那真是一个可怕的东西。

为了避免受到不必要的攻击,请大家及时检查所有联网设备的管理端的密码,如果有使用默认密码及弱口令,立即进行密码的更改,以保证服务的安全。

– EOF –

转自:https://mp.weixin.qq.com/s/wQjXtZCj-uY3L63husLDTg

marshmallow,支持 Python 对象和原生数据相互转换的库

在很多情况下,我们会有把 Python 对象进行序列化或反序列化的需求,比如开发 REST API,比如一些面向对象化的数据加载和保存,都会应用到这个功能。

这里看一个最基本的例子,这里给到一个 User 的 Class 定义,再给到一个 data 数据,像这样:

class User(object):
def __init__(self, name, age):
self.name = name
self.age = age

data = [{
'name': 'Germey',
'age': 23
}, {
'name': 'Mike',
'age': 20
}]

现在我要把这个 data 快速转成 User 组成的数组,变成这样:

[User(name='Germey', age=23), User(name='Mike', age=20)]

你会怎么来实现?

或者我有了上面的列表内容,想要转成一个 JSON 字符串,变成这样:

[{"name": "Germey", "age": 23}, {"name": "Mike", "age": 20}]

你又会怎么操作呢?

另外如果 JSON 数据里面有各种各样的脏数据,你需要在初始化时验证这些字段是否合法,另外 User 这个对象里面 name、age 的数据类型不同,如何针对不同的数据类型进行针对性的类型转换,这个你有更好的实现方案吗?

初步思路

之前我介绍过 attrs 和 cattrs 这两个库,它们二者的组合可以非常方便地实现对象的序列化和反序列化。

譬如这样:

from attr import attrs, attrib
from cattr import structure, unstructure

@attrs
class User(object):
name = attrib()
age = attrib()

data = {
'name': 'Germey',
'age': 23
}
user = structure(data, User)
print('user', user)
json = unstructure(user)
print('json', json)

运行结果:

user User(name='Germey', age=23)
json {'name': 'Germey', 'age': 23}

好,这里我们通过 attrs 和 cattrs 这两个库来实现了单个对象的转换。

首先我们要肯定一下 attrs 这个库,它可以极大地简化 Python 类的定义,同时每个字段可以定义多种数据类型。

但 cattrs 这个库就相对弱一些了,如果把 data 换成数组,用 cattrs 还是不怎么好转换的,另外它的 structure 和 unstructure 在某些情景下容错能力较差,所以对于上面的需求,用这两个库搭配起来并不是一个最优的解决方案。

另外数据的校验也是一个问题,attrs 虽然提供了 validator 的参数,但对于多种类型的数据处理的支持并没有那么强大。

所以,我们想要寻求一个更优的解决方案。

更优雅的方案

这里推荐一个库,叫做 marshmallow,它是专门用来支持 Python 对象和原生数据相互转换的库,如实现 object -> dict,objects -> list, string -> dict, string -> list 等的转换功能,另外它还提供了非常丰富的数据类型转换和校验 API,帮助我们快速实现数据的转换。

要使用 marshmallow 这个库,需要先安装下:

pip3 install marshmallow

好了之后,我们在之前的基础上定义一个 Schema,如下:

class UserSchema(Schema):
name = fields.Str()
age = fields.Integer()

@post_load
def make(self, data, **kwargs):
return User(**data)

还是之前的数据:

data = [{
'name': 'Germey',
'age': 23
}, {
'name': 'Mike',
'age': 20
}]

这时候我们只需要调用 Schema 的 load 事件就好了:

schema = UserSchema()
users = schema.load(data, many=True)
print(users)

输出结果如下:

[User(name='Germey', age=23), User(name='Mike', age=20)]

这样,我们非常轻松地完成了 JSON 到 User List 的转换。

有人说,如果是单个数据怎么办呢,只需要把 load 方法的 many 参数去掉即可:

data = {
'name': 'Germey',
'age': 23
}

schema = UserSchema()
user = schema.load(data)
print(user)

输出结果:

User(name='Germey', age=23)

当然,这仅仅是一个反序列化操作,我们还可以正向进行序列化,以及使用各种各样的验证条件。

下面我们再来看看吧。

更方便的序列化

上面的例子我们实现了序列化操作,输出了 users 为:

[User(name='Germey', age=23), User(name='Mike', age=20)]

有了这个数据,我们也能轻松实现序列化操作。

序列化操作,使用 dump 方法即可

result = schema.dump(users, many=True)
print('result', result)

运行结果如下:

result [{'age': 23, 'name': 'Germey'}, {'age': 20, 'name': 'Mike'}]

由于是 List,所以 dump 方法需要加一个参数 many 为 True。

当然对于单个对象,直接使用 dump 同样是可以的:

result = schema.dump(user)
print('result', result)

运行结果如下:

result {'name': 'Germey', 'age': 23}

这样的话,单个、多个对象的序列化也不再是难事。

经过上面的操作,我们完成了 object 到 dict 或 list 的转换,即:

object <-> dict
objects <-> list

验证

当然,上面的功能其实并不足以让你觉得 marshmallow 有多么了不起,其实就是一个对象到基本数据的转换嘛。但肯定不止这些,marshmallow 还提供了更加强大啊功能,比如说验证,Validation。

比如这里我们将 age 这个字段设置为 hello,它无法被转换成数值类型,所以肯定会报错,样例如下:

data = {
'name': 'Germey',
'age': 'hello'
}

from marshmallow import ValidationError
try:
schema = UserSchema()
user, errors = schema.load(data)
print(user, errors)
except ValidationError as e:
print('e.message', e.messages)
print('e.valid_data', e.valid_data)

这里如果加载报错,我们可以直接拿到 Error 的 messages 和 valid_data 对象,它包含了错误的信息和正确的字段结果,运行结果如下:

e.message {'age': ['Not a valid integer.']}
e.valid_data {'name': 'Germey'}

因此,比如我们想要开发一个功能,比如用户注册,表单信息就是提交过来的 data,我们只需要过一遍 Validation,就可以轻松得知哪些数据符合要求,哪些不符合要求,接着再进一步进行处理。

当然验证功能肯定不止这一些,我们再来感受一下另一个示例:

from pprint import pprint
from marshmallow import Schema, fields, validate, ValidationError

class UserSchema(Schema):
name = fields.Str(validate=validate.Length(min=1))
permission = fields.Str(validate=validate.OneOf(['read', 'write', 'admin']))
age = fields.Int(validate=validate.Range(min=18, max=40))


in_data = {'name': '', 'permission': 'invalid', 'age': 71}
try:
UserSchema().load(in_data)
except ValidationError as err:
pprint(err.messages)

比如这里的 validate 字段,我们分别校验了 name、permission、age 三个字段,校验方式各不相同。

如 name 我们要判断其最小值为 1,则使用了 Length 对象。permission 必须要是几个字符串之一,这里又使用了 OneOf 对象,age 又必须是介于某个范围之间,这里就使用了 Range 对象。

下面我们故意传入一些错误的数据,看下运行结果:

{'age': ['Must be greater than or equal to 18 and less than or equal to 40.'],
'name': ['Shorter than minimum length 1.'],
'permission': ['Must be one of: read, write, admin.']}

可以看到,这里也返回了数据验证的结果,对于不符合条件的字段,一一进行说明。

另外我们也可以自定义验证方法:

from marshmallow import Schema, fields, ValidationError

def validate_quantity(n):
if n < 0:
raise ValidationError('Quantity must be greater than 0.')
if n > 30:
raise ValidationError('Quantity must not be greater than 30.')

class ItemSchema(Schema):
quantity = fields.Integer(validate=validate_quantity)

in_data = {'quantity': 31}
try:
result = ItemSchema().load(in_data)
except ValidationError as err:
print(err.messages)

通过自定义方法,同样可以实现更灵活的验证,运行结果:

{'quantity': ['Quantity must not be greater than 30.']}

对于上面的例子,还有更优雅的写法:

from marshmallow import fields, Schema, validates, ValidationError


class ItemSchema(Schema):
quantity = fields.Integer()

@validates('quantity')
def validate_quantity(self, value):
if value < 0:
raise ValidationError('Quantity must be greater than 0.')
if value > 30:
raise ValidationError('Quantity must not be greater than 30.')

通过定义方法并用 validates 修饰符,使得代码的书写更加简洁。

必填字段

如果要想定义必填字段,只需要在 fields 里面加入 required 参数并设置为 True 即可,另外我们还可以自定义错误信息,使用 error_messages 即可,例如:

from pprint import pprint
from marshmallow import Schema, fields, ValidationError

class UserSchema(Schema):
name = fields.String(required=True)
age = fields.Integer(required=True, error_messages={'required': 'Age is required.'})
city = fields.String(
required=True,
error_messages={'required': {'message': 'City required', 'code': 400}},
)
email = fields.Email()

try:
result = UserSchema().load({'email': 'foo@bar.com'})
except ValidationError as err:
pprint(err.messages)

默认字段

对于序列化和反序列化字段,marshmallow 还提供了默认值,而且区分得非常清楚!如 missing 则是在反序列化时自动填充的数据,default 则是在序列化时自动填充的数据。

例如:

from marshmallow import Schema, fields
import datetime as dt
import uuid

class UserSchema(Schema):
id = fields.UUID(missing=uuid.uuid1)
birthdate = fields.DateTime(default=dt.datetime(2017, 9, 29))

print(UserSchema().load({}))
print(UserSchema().dump({}))

这里我们都是定义的空数据,分别进行序列化和反序列化,运行结果如下:

{'id': UUID('06aa384a-570c-11ea-9869-a0999b0d6843')}
{'birthdate': '2017-09-29T00:00:00'}

可以看到,在没有真实值的情况下,序列化和反序列化都是用了默认值。

这个真的是解决了我之前在 cattrs 序列化和反序列化时候的痛点啊!

指定属性名

在序列化时,Schema 对象会默认使用和自身定义相同的 fields 属性名,当然也可以自定义,如:

class UserSchema(Schema):
name = fields.String()
email_addr = fields.String(attribute='email')
date_created = fields.DateTime(attribute='created_at')

user = User('Keith', email='keith@stones.com')
ser = UserSchema()
result, errors = ser.dump(user)
pprint(result)

运行结果如下:

{'name': 'Keith',
'email_addr': 'keith@stones.com',
'date_created': '2014-08-17T14:58:57.600623+00:00'}

反序列化也是一样,例如:

class UserSchema(Schema):
name = fields.String()
email = fields.Email(load_from='emailAddress')

data = {
'name': 'Mike',
'emailAddress': 'foo@bar.com'
}
s = UserSchema()
result, errors = s.load(data)

运行结果如下:

{'name': u'Mike',
'email': 'foo@bar.com'}

嵌套属性

对于嵌套属性,marshmallow 当然也不在话下,这也是让我觉得 marshmallow 非常好用的地方,例如:

from datetime import date
from marshmallow import Schema, fields, pprint

class ArtistSchema(Schema):
name = fields.Str()

class AlbumSchema(Schema):
title = fields.Str()
release_date = fields.Date()
artist = fields.Nested(ArtistSchema())

bowie = dict(name='David Bowie')
album = dict(artist=bowie, title='Hunky Dory', release_date=date(1971, 12, 17))

schema = AlbumSchema()
result = schema.dump(album)
pprint(result, indent=2)

这样我们就能充分利用好对象关联外键来方便地实现很多关联功能。

以上介绍的内容基本算在日常的使用中是够用了,当然以上都是一些基本的示例,对于更多功能,可以参考 marchmallow 的官方文档:https://marshmallow.readthedocs.io/en/stable/,强烈推荐大家用起来。

– EOF –