30分钟搭建Python的Flask框架并在上面编写第一个应用


Posted in Python onMarch 30, 2015

Flask 是一种很赞的Python web框架。它极小,简单,最棒的是它很容易学。
30分钟搭建Python的Flask框架并在上面编写第一个应用今天我来带你搭建你的第一个Flask web应用!和官方教程 一样,你将搭建你自己的微博客系统:Flaskr。和官方Flask教程不同的是——我们通过使用Stormpath来创建并管理用户账户和数据,你的工作效率会更高。开发进程会显著地加快!

我们这就开始吧。

注意:这篇教程面向Flask开发新人,帮助他们理解如何使用Flask和Stormpath建立一个简单的网站。本文是Flask官方教程的改版。
目标

Flaskr 应用的目标很简单:

  •     允许用户以事先生成的账户(使用Stormpath保存)登录、登出本微博客系统。
  •     允许登入用户在页面添加包含了纯文本和一些HTML body文本的条目。用户被认为是可信任的,HTML内容不会被审查。
  •     以时间顺序(新的在最上)在网站主页显示所有博客条目。

最终的网站应该看起来像这样:
30分钟搭建Python的Flask框架并在上面编写第一个应用准备

开始之前,我们需要先安装一些Python包才能干活!我们通过?Python包管理器pip来完成这件事。
 pip install flask flask-stormpath

上述命令会安装两个包:Flask, 和Flask-Stormpath,这两个包在整篇教程都会用到。

接着,你需要创建一个Stormpath账户。你可以在Stormpath网站注册:https://api.stormpath.com/register

当你创建了一个Stormpath账户并登入后,你还需要创建一个API 密钥。你可以在操作页面上点击创建API密钥按钮来完成:https://api.stormpath.com/ui/dashboard

创建了API密钥后,你会被提示要去下载一个名为apiKey.properties的文件,稍后我们将用到它。

注意:不要把apiKey.properties文件检入你的版本控制系统(如果你在用的话)!这个文件存储着你的Stormpath证书,应该被妥善保管。

接着,你会想要创建新的Stormpath应用,请访问应用网页:https://api.stormpath.com/v#!applications?,点击注册应用。创建一个名为flaskr的新应用,选项按默认的来。

最后,访问账户页面:https://api.stormpath.com/ui/accounts/create? ,在flaskr Directory创建新用户账户。所有在这儿创建的账户都可以用来登入你将搭建的微博客。
目录结构

你要做的第一件事就是创建一个存放你应用代码的目录结构。你需要创建若干目录,然后把你的apiKey.properites?放到新建的项目目录:
 

$ mkdir -p flaskr/{static,templates}; cp ~/path/to/apiKey.properties flaskr/
$ tree flaskr
flaskr
├── apiKey.properties
├── static
└── templates
 
2 directories, 1 file

flaskr目录将是你应用的根目录。static目录用于存放你的静态文件(css,javascript, 和图像文件).templates目录用于存放你的Jinja模板(用于渲染HTML)。
安装应用

现在你的目录结构搭好了,我们开始配置应用了!

首先,在你flaskr目录下创建名为flaskr.py的新文件。你的应用代码放在这个文件里。

一切将从这里开始:

from datetime import datetime
 
from flask import (
 Flask,
 abort,
 flash,
 redirect,
 render_template,
 request,
 url_for,
)
from flask.ext.stormpath import (
 StormpathError,
 StormpathManager,
 User,
 login_required,
 login_user,
 logout_user,
 user,
)
 
app = Flask(__name__)
app.config['DEBUG'] = True
app.config['SECRET_KEY'] = 'some_really_long_random_string_here'
app.config['STORMPATH_API_KEY_FILE'] = 'apiKey.properties'
app.config['STORMPATH_APPLICATION'] = 'flaskr'
 
stormpath_manager = StormpathManager(app)
 
if __name__ == '__main__':
 app.run()

需要关注的:

  •     在flaskr.py文件的开头你导入了若干库——下面整篇教程都会用到它们
  •     你创建了一个app对象——这是每个Flask 项目的核心
  •     你在app.config里添加了若干配置变量。app.config是一个Python字典变量,你可以用它存放你想存放的任何定制配置。这里我们设置了若干后面会用到的重要的变量:
  •         DEBUG 变量可以设置为True或者False。它用于控制Flask的内置错误报告行为(开发模式下它让Flask可以显示详细的错误信息)
  •         SECRET_KEY 变量在内部使用,用来保证客户端的会话安全。当部署一个真正的Flask应用时,要确保这是一个较长的随机生成的字符串。
  •         STORMPATH_API_KEY_FILE? 变量应该指向你的apiKey.properties 文件位置。更多信息参见:http://flask-stormpath.readthedocs.org/en/latest/setup.html
  •         STORMPATH_APPLICATION 变量应该是你之前创建的Stormpath 应用名。
  •     你创建了一个stormpath_manager 对象,它用于控制Stormpath 库,后面将帮你轻松与用户以及用户数据进行交互。
  •     You're calling?app.run()?at the bottom. This tells Flask to run your site in development mode for testing.
  •     在最后你调用了app.run()?。它告诉Flask以开发模式运行你的网站以便于测试。

运行下述命令之后,你就可以看到你的Flask应用开始在端口5000运行:

$ python flaskr.py
 * Running on http://127.0.0.1:5000/
 * Restarting with reloader

不过当你访问http://127.0.0.1:5000, 你会看到一个404 not found 信息。这是因为你还没有定义任何视图或者URL路由 。
视图

现在安装的部分你已经做完了,我们来定义视图。下面的代码应该放在flaskr.py 文件中,在这个上面:

if __name__ == ‘__main__':
 app.run()

代码如下:

@app.route('/')
def show_posts():
 posts = []
 for account in stormpath_manager.application.accounts:
  if account.custom_data.get('posts'):
   posts.extend(account.custom_data['posts'])
 
 posts = sorted(posts, key=lambda k: k['date'], reverse=True)
 return render_template('show_posts.html', posts=posts)
 
@app.route('/add', methods=['POST'])
@login_required
def add_post():
 if not user.custom_data.get('posts'):
  user.custom_data['posts'] = []
 
 user.custom_data['posts'].append({
  'date': datetime.utcnow().isoformat(),
  'title': request.form['title'],
  'text': request.form['text'],
 })
 user.save()
 
 flash('New post successfully added.')
 return redirect(url_for('show_posts'))
 
@app.route('/login', methods=['GET', 'POST'])
def login():
 error = None
 
 if request.method == 'POST':
  try:
   _user = User.from_login(
    request.form['email'],
    request.form['password'],
   )
   login_user(_user, remember=True)
   flash('You were logged in.')
 
   return redirect(url_for('show_posts'))
  except StormpathError, err:
   error = err.message
 
 return render_template('login.html', error=error)
 
@app.route('/logout')
def logout():
 logout_user()
 flash('You were logged out.')
 
 return redirect(url_for('show_posts'))

我们来讨论一下上面的代码。

你可能注意到首先定义的函数是show_posts。这个函数用于在网站前端页面显示发布的博文。如你可能已经猜到的,装饰器@app.route(‘/')告诉Flask如何运行这个函数。

每次用户请求 URL “/”,Flask就会运行show_posts,把输出返回给用户。

show_posts 就只是:

  •     迭代所有的用户账户,查找博文。每篇博文就是一个简单的如下格式的Python字典: 
{
 'date': '2014-04-01T22:50:49.762475',
 'text': 'Blog content.',
 'title': 'Post title'
}
  •     每找到一篇博文就添加到posts数组。
  •     按日期对posts数组排序,因此新发布的在前面。
  •     以posts数组作为输入,渲染出一个叫做show_posts.html的HTML模板.
  • add_posts 视图用于登入用户向网站发布新博文。这个视图带来下面几样东西:
  •     装饰器@app.route(‘/add', methods=[‘POST'])告诉Flask该URL只接受POST请求。Flask默认只接受GET请求。
  •     @login_required 装饰器确保能访问该视图前用户已经登入。如果用户没有登入而尝试对该视图POST,会得到HTTP 401 未授权应答。
  •     任何装饰了?@login_required的视图都可以访问user变量。这是一个存放了用户账户细节的对象。

它的工作机制很简单:

  •     检查用户账户下是否有保存的博文。这一步是通过检查user.custom_data.get(‘posts') 不是False来实现的。user.custom_data是一个Python字典,你可以在其中保存任何想保存的用户数据。
  •     从POST 请求中抓取标题和正文,在用户的posts数组中创建一个新的post对象。
  •     保存新博文到用户账户的Stormpath中。
  •     发出一个后面会向用户展示的消息。
  •     最后,把用户重定向到show_posts?视图,让新添加的博文可以展现出来。
  •     login 和logout视图特别地简单。

login视图就是简单地从用户POST请求中提取电子邮件地址和密码,然后从Stormpath抓取user对象,尝试登录,并创建一个本地会话。

logout视图就是销毁用户会话。
模板

下一样要加入的东西就是模板代码。Flask使用Jinja模板语言,它让编写HTML模板变得非常简单。

让我们定义一个布局模板templates/layout.html作为开始。后面我们写的其他模板都将从这个基础模板而来。这个策略很有用,因为它允许你在一个地方定义会被多次引用的模板代码。

把下面的代码添加到你的layout.html模板文件:
 

<!doctype html>
<title>Flaskr</title>
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='style.css') }}">
<div>
 <h1>Flaskr</h1>
 <div>
 {% if user.email %}
 <a href="{{ url_for('logout') }}">log out</a>
 {% else %}
 <a href="{{ url_for('login') }}">log in</a>
 {% endif %}
 </div>
 {% for message in get_flashed_messages() %}
 <div>{{ message }}</div>
 {% endfor %}
 {% block body %}{% endblock %}
</div>

接着是templates/show_posts.html 模板文件:
 

{% extends "layout.html" %}
{% block body %}
 {% if user.email %}
 <form action="{{ url_for('add_post') }}" method=post>
  <dl>
  <dt>Title:
  <dd><input type=text size=30 name=title>
  <dt>Text:
  <dd><textarea name=text rows=5 cols=40></textarea>
  <dd><input type=submit value=Share>
  </dl>
 </form>
 {% endif %}
 <ul>
 {% for post in posts %}
 <li><h2>{{ post['title'] }}</h2>{{ post['text']|safe }}
 {% else %}
 <li><em>Unbelievable. No posts here so far!</em>
 {% endfor %}
 </ul>
{% endblock %}

最后,是templates/login.html 模板文件:
 

{% extends "layout.html" %}
{% block body %}
 <h2>Login</h2>
 {% if error %}<p><strong>Error:</strong> {{ error }}{% endif %}
 <form action="{{ url_for('login') }}" method=post>
 <dl>
  <dt>Email:
  <dd><input type=text name=email>
  <dt>Password:
  <dd><input type=password name=password>
  <dd><input type=submit value=Login>
 </dl>
 </form>
{% endblock %}

首先要注意的是?layout.html模板定义了一个body块,在任何子模板中它都可以被同名的块替代。

layout.html模板显示了一个login或者logout模板,还显示所有的闪回信息。
 

{% if user.email %}

因为你用的是Flask-Stormpath,所有模板都可以访问一个神奇的user 变量。当一个用户登入后,该用户的所有细节都是可见的(因此这个检查能生效)。

show_posts.html?模板迭代posts数组,它是被show_posts视图调用render_template时传入的。Jinja允许你对任何可以迭代的东西使用for循环。

还有很重要的一点,为了输出变量内容,你需要在变量外面加上花括号.
 

{{ variable }}
 
{{ post['text']|safe }}

既然我们决定了允许用户在他们的博文中输入任性的HTML代码,我们就得使用模板的safe过滤器来输出博文中的body块。

Jinja默认会自动忽略任何特殊字符,所以我们得使用safe过滤器来显示用户输入的HTML和Javascript。
添加样式

最后一件要做的就是创建一个带有如下内容的static/style.css 文件:
 

body   { font-family: sans-serif; background: #eee; }
a, h1, h2  { color: #377ba8; }
h1, h2   { font-family: 'Georgia', serif; margin: 0; }
h1    { border-bottom: 2px solid #eee; }
h2    { font-size: 1.2em; }
 
.page   { margin: 2em auto; width: 35em; border: 5px solid #ccc;
     padding: 0.8em; background: white; }
.entries  { list-style: none; margin: 0; padding: 0; }
.entries li  { margin: 0.8em 1.2em; }
.entries li h2 { margin-left: -1em; }
.add-entry  { font-size: 0.9em; border-bottom: 1px solid #ccc; }
.add-entry dl { font-weight: bold; }
.metanav  { text-align: right; font-size: 0.8em; padding: 0.3em;
     margin-bottom: 1em; background: #fafafa; }
.flash   { background: #cee5F5; padding: 0.5em;
     border: 1px solid #aacbe2; }
.error   { background: #f0d6d6; padding: 0.5em; }

这个文件会被layout.html 模板加载,提供得体的显示风格。
测试它

现在我们完成了应用的编码,让我们看看最终的产品吧!

为了运行你炫酷的新网站,你得首先通过如下命令来重新启动Flask web 服务器:
 

$ python flaskr.py
 * Running on http://127.0.0.1:5000/
 * Restarting with reloader

然后在你的浏览器访问http://127.0.0.1:5000吧。你现在应该能看到正在运行的flaskr 网站,并能使用Stormpath 账户登入,发博文等等。

Python 相关文章推荐
学习python处理python编码问题
Mar 13 Python
Python装饰器入门学习教程(九步学习)
Jan 28 Python
python常见的格式化输出小结
Dec 15 Python
Python使用plotly绘制数据图表的方法
Jul 18 Python
使用Python制作简单的小程序IP查看器功能
Apr 16 Python
解决python tkinter界面卡死的问题
Jul 17 Python
使用pytorch完成kaggle猫狗图像识别方式
Jan 10 Python
python3利用Axes3D库画3D模型图
Mar 25 Python
python传到前端的数据,双引号被转义的问题
Apr 03 Python
Pymysql实现往表中插入数据过程解析
Jun 02 Python
Vs Code中8个好用的python 扩展插件
Oct 12 Python
利用pipenv和pyenv管理多个相互独立的Python虚拟开发环境
Nov 01 Python
编写同时兼容Python2.x与Python3.x版本的代码的几个示例
Mar 30 #Python
以Python的Pyspider为例剖析搜索引擎的网络爬虫实现方法
Mar 30 #Python
在树莓派2或树莓派B+上安装Python和OpenCV的教程
Mar 30 #Python
Python中利用函数装饰器实现备忘功能
Mar 30 #Python
利用Python绘制MySQL数据图实现数据可视化
Mar 30 #Python
Python面向对象编程中的类和对象学习教程
Mar 30 #Python
详细介绍Python函数中的默认参数
Mar 30 #Python
You might like
一个php短网址的生成代码(仿微博短网址)
2014/05/07 PHP
php禁止某ip或ip地址段访问的方法
2015/02/25 PHP
PHP开启opcache提升代码性能
2015/04/26 PHP
变量在 PHP7 内部的实现(二)
2015/12/21 PHP
php数组冒泡排序算法实例
2016/05/06 PHP
PHP面相对象中的重载与重写
2017/02/13 PHP
nginx 设置多个站跨域
2021/03/09 Servers
让你的网站可编辑的实现js代码
2009/10/19 Javascript
javascript 哈希表(hashtable)的简单实现
2010/01/20 Javascript
学习js在线html(富文本,所见即所得)编辑器
2012/12/18 Javascript
jquery 面包屑导航 具体实现
2013/06/05 Javascript
JavaScript按位运算符的应用简析
2014/02/04 Javascript
浅谈vue的踩坑路
2017/08/31 Javascript
javascript观察者模式实现自动刷新效果
2017/09/05 Javascript
seaJs使用心得之exports与module.exports的区别实例分析
2017/10/13 Javascript
详解Node.js读写中文内容文件操作
2018/10/10 Javascript
CountUp.js实现数字滚动增值效果
2019/10/17 Javascript
vue中touch和click共存的解决方式
2020/07/28 Javascript
Python读csv文件去掉一列后再写入新的文件实例
2017/12/28 Python
python 使用while写猜年龄小游戏过程解析
2019/10/07 Python
PyTorch的自适应池化Adaptive Pooling实例
2020/01/03 Python
使用keras和tensorflow保存为可部署的pb格式
2020/05/25 Python
pycharm 添加解释器的方法步骤
2020/08/31 Python
PyQT5速成教程之Qt Designer介绍与入门
2020/11/02 Python
Python eval函数介绍及用法
2020/11/09 Python
来自Ocado的宠物商店:Fetch
2018/07/10 全球购物
瑞典在互联网上最大的宠物商店:Animail
2020/10/31 全球购物
教师群众路线教育实践活动学习笔记
2014/11/05 职场文书
2014年统计工作总结
2014/11/21 职场文书
小学感恩节活动总结
2015/03/24 职场文书
《月光曲》教学反思
2016/02/16 职场文书
微信小程序基础教程之echart的使用
2021/06/01 Javascript
Mysql关于数据库是否应该使用外键约束详解说明
2021/10/24 MySQL
Golang 入门 之url 包
2022/05/04 Golang
HTML页面中使两个div并排显示的实现
2022/05/15 HTML / CSS
Promise静态四兄弟实现示例详解
2022/07/07 Javascript