Flask web开发-基于Python的Web应用开发实战-MacOS

第一章 安装

1.安装virtualenv

$ pip install virtualenv  // or $ sudo easy_install virtualenv 
$ virtualenv --version

2.Clone source code to local

$ cd flask-2017   //create a folder named flask-2017
$ git clone  git@github.com:baby4bamboo/flasky.git 
$ cd flasky
$ git checkout 1a

3.虚拟环境

$ virtualenv venv  //创建虚拟环境
$ source venv/bin/activate  //启动虚拟环境
$ deactivate //关闭虚拟环境

4.需要安装的Flask 相关的包:

$ pip install flask //MarkupSafe, Jinja2, click, Werkzeug, itsdangerous, flask

5.python 命令行:

$ python
>>> import flask   //检查某个包是否安装好
>>> exit()  //退出python命令行

第二章 程序的基本结构

Web 服务器使用一种名为 Web 服务器网关接口
(Web Server Gateway Interface,WSGI)的协议,把接收自客户端的所有请求都转交给这个对象处理。

git checkout 2b

from flask import Flask
app = Flask(__name__)

@app.route('/')
def index():
    return '<h1>Hello World!</h1>'

@app.route('/user/<name>')
def user(name):
    return '<h1>Hello, %s!</h1>' % name

if __name__ == '__main__':
    app.run(debug=True)

__name__=='__main__'是 Python 的惯常用法,在这里确保直接执行这个脚本时才启动开发Web 服务器。如果这个脚本由其他脚本引入,程序假定父级脚本会启动不同的服务器,因此不会执行 app.run()

python hello.py  //启动程序
Ctrl + C //退出程序

然后浏览器访问
http://127.0.0.1:5000/

请求上下文

请求上下文.png
>>> app_ctx = app.app_context()
>>> app_ctx.push()
>>> current_app.name
'hello'
>>> app_ctx.pop()
路由调度

Flask 使用 app.route 修饰器或者非修饰器形式的 app.add_url_rule() 生成映射。

 $ python
>>> from hello import app
>>> app.url_map
Map([<Rule '/' (HEAD, OPTIONS, GET) -> index>,
 <Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
 <Rule '/user/<name>' (HEAD, OPTIONS, GET) -> user>])
Hook
• before_first_request:注册一个函数,在处理第一个请求之前运行。
• before_request:注册一个函数,在每次请求之前运行。
• after_request:注册一个函数,如果没有未处理的异常抛出,在每次请求之后运行。
• teardown_request:注册一个函数,即使有未处理的异常抛出,也在每次请求之后运行
响应

Flask将视图函数的返回值,作为响应结果,通常就是一个字符串
也可以接受第二个参数,为响应的状态码,例如200,404等
也可以接受第三个参数,是一个由首部(header)组成的字典

@app.route('/')
def index():
 return '<h1>Bad Request</h1>', 400

当然,更好的选择是返回一个respones对象

from flask import make_response
@app.route('/')
def index():
 response = make_response('<h1>This document carries a cookie!</h1>')
 response.set_cookie('answer', '42')
 return response

有一种特殊的响应,叫做重定向(14章会有例子)

from flask import redirect
@app.route('/')
def index():
 return redirect('http://www.example.com')

还有一种特殊的响应,由abort()函数生成,用来处理错误
abort 不会把控制权交还给调用它的函数,而是抛出异常把控制权交给 Web 服务器

from flask import abort
@app.route('/user/<id>')
def get_user(id):
 user = load_user(id)
 if not user:
 abort(404)
 return '<h1>Hello, %s</h1>' % user.name
Flask扩展
$ pip install flask-script

文件的改动:

from flask.ext.script import Manager
manager = Manager(app)
# ...
if __name__ == '__main__':
 manager.run()

这个扩展的初始化方法也适用于其他很多扩展:把程序实例作为参数传给构造函数,初始化主类的实例。创建的对象可以在各个扩展中使用。在这里,服务器由 manager.run() 启动,启动后就能解析命令行了。

python hello.py runserver --host 0.0.0.0  //监听所有窗口

第三章 模板

面对一个请求,其实server做了两件事情,分成‘业务逻辑’和‘表现逻辑’

例如用户输入了账号密码,点击登录
业务逻辑:在检查账号密码是否匹配,如果匹配成功则登录
表现逻辑:跳转到已经登录用户的首页

Jinja2模板引擎

Jinja有两种定界符。{% ... %}和{{ ... }}。前者用于执行像for循环或赋值等语句,后者向模板输出一个表达式的结果。
参见http://jinja.pocoo.org/docs/templates/#synopsis

Jinja2变量过滤器.png

if

{% if user %}
 Hello, {{ user }}!
{% else %}
 Hello, Stranger!
{% endif %}

for

<ul>
 {% for comment in comments %}
 <li>{{ comment }}</li>
 {% endfor %}
</ul>

macro

{% macro render_comment(comment) %}
 <li>{{ comment }}</li>
{% endmacro %}
<ul>
 {% for comment in comments %}
 {{ render_comment(comment) }}
 {% endfor %}
</ul>

单独文件导入

{% import 'macros.html' as macros %}
<ul>
 {% for comment in comments %}
 {{ macros.render_comment(comment) }}
 {% endfor %}
</ul>

模板继承

base.html

<html>
<head>
 {% block head %}
 <title>{% block title %}{% endblock %} - My Application</title>
 {% endblock %}
</head>
<body>
 {% block body %}
 {% endblock %}
</body>
</html>

extend.html

{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
 {{ super() }}
 <style>
 </style>
{% endblock %}
{% block body %}
<h1>Hello, World!</h1>
{% endblock %}
模板渲染

<h1>Hello, {{ name }}!</h1>

@app.route('/user/<name>')
def user(name):
 return render_template('user.html', name=name)

name=name,左边的name表示模板中的占位符,右边的name表示url中传进来的参数

使用Flask-Bootstrap集成Twitter Bootstrap
 $ pip install flask-bootstrap

hello.py:初始化 Flask-Bootstrap

from flask.ext.bootstrap import Bootstrap
# ...
bootstrap = Bootstrap(app)

doc 整个 HTML 文档
html_attribs <html> 标签的属性
html <html> 标签中的内容
head <head> 标签中的内容
title <title> 标签中的内容
metas 一组 <meta> 标签
styles 层叠样式表定义
body_attribs <body> 标签的属性
body <body> 标签中的内容
navbar 用户定义的导航条
content 用户定义的页面内容
scripts 文档底部的 JavaScript 声明

链接
url_for('index')             //得到相对路径,这里会得到'/'
url_for('index', _external=True)       //得到绝对路径,这里是'http://localhost:5000/'。(外部使用的时候要用,例如邮件)
url_for('user', name='john', _external=True)   //返回结果是 http://localhost:5000/user/john。
url_for('index', page=2)   // 返回结果是 /?page=2
使用Flask-Moment本地化日期和时间

1.安装

pip install flask-moment

2.hello.py 中初始化Moment:

from flask.ext.moment import Moment
moment = Moment(app)

3.基模板(templates/base.html)中引入moment.js

{% block scripts %}
{{ super() }}
{{ moment.include_moment() }}
{% endblock %}

4.把变量current_time 传入模板进行渲染

from datetime import datetime
@app.route('/')
def index():
 return render_template('index.html',current_time=datetime.utcnow())

5.在模板中渲染 current_time
templates/index.html

{% block page_content %} //page_content 表示显示的内容,之前错误的放在了{% block scripts %}中,发现不能在页面显示
<p>The local date and time is {{ moment(current_time).format('LLL') }}</p>
<p>That was {{ moment(current_time).fromNow(refresh=True) }}</p>
{% endblock %}

第四章 web表单

Flask-WTF 能保护所有表单免受跨站请求伪造(Cross-Site Request Forgery,CSRF)的攻击
hello.py

app = Flask(__name__)
app.config['SECRET_KEY'] = 'hard to guess string'
 $ pip install flask-wtf
WTForms支持的HTML字段类型.png
WTForms验证函数.png

M

from flask.ext.wtf import Form
from wtforms import StringField, SubmitField
from wtforms.validators import Required
class NameForm(Form):
 name = StringField('What is your name?', validators=[Required()])
 submit = SubmitField('Submit')

T

templates/index.html

{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Flasky{% endblock %}
{% block page_content %}
<div class="page-header">
 <h1>Hello, {% if name %}{{ name }}{% else %}Stranger{% endif %}!</h1>
</div>
{{ wtf.quick_form(form) }}
{% endblock %}

V

hello.py:

@app.route('/', methods=['GET', 'POST'])
def index():
 name = None
 form = NameForm()    //建一个M
 if form.validate_on_submit():
   name = form.name.data
   form.name.data = ''
 return render_template('index.html', form=form, name=name)  //把得到的数据传给T

MTV:
M是Model,数据结构,
T是template,模板,最后渲染出来的页面的样子
V是view,先建一个M,然后通过一系列的逻辑和运算,得到需要的数据,传给T

注:这里我写的时候出现了三个错误

app.config['SECRET_KEY'] = 'hard to guess string'  //这句忘记加了
{{ wtf.quick_form(form) }}  //写成了{% wtf.quick_form(form) %}
name = form.name.data    //写成了 name = form.name.data()

session and 重定向

from flask import Flask, render_template, session, redirect, url_for
@app.route('/', methods=['GET', 'POST'])
def index():
 form = NameForm()
 if form.validate_on_submit():
   session['name'] = form.name.data
   return redirect(url_for('index'))
 return render_template('index.html', form=form, name=session.get('name'))

hello.py flash消息

from flask import Flask, render_template, session, redirect, url_for, flash
@app.route('/', methods=['GET', 'POST'])
def index():
     form = NameForm()
     if form.validate_on_submit():
         old_name = session.get('name')
         if old_name is not None and old_name != form.name.data:
             flash('Looks like you have changed your name!')
             session['name'] = form.name.data
             return redirect(url_for('index'))
 return render_template('index.html',form = form, name = session.get('name'))

base.html

{% block content %}
<div class="container">
  {% for message in get_flashed_messages() %}
  <div class="alert alert-warning">
    <button type="button" class="close" data-dismiss="alert">×
    </button>
    {{ message }}
  </div>
  {% endfor %}
  {% block page_content %}{% endblock %}
</div>
{% endblock %}

第七章 web表单

项目结构


最基础的目录结构

工厂函数

app/__init__.py :

  bootstrap = Bootstrap()
  mail = Mail()
  moment = Moment()
  db = SQLAlchemy()

def create_app(config_name):
    app = Flask(__name__)   //实例化一个Flask对象,命名为app
    app.config.from_object(config[config_name])  //把我们配置的config,设置给这个app
    config[config_name].init_app(app)  //config中定义的初始化函数

    bootstrap.init_app(app)
    mail.init_app(app)
    moment.init_app(app)
    db.init_app(app)

    from .main import main as main_blueprint //在app中,注册blueprint
    app.register_blueprint(main_blueprint)

    return app

blueprint

app/main/__init__.py:创建蓝本:

from flask import Blueprint
main = Blueprint('main', __name__)
from . import views, errors

实例化一个蓝本,命名为main,接受两个参数,第一个是蓝本的名字,第二个是蓝本所在的包或模块,一般都是__name__
views(路由设定),errors(错误处理)必须在创建蓝本之后导入,以防止循环依赖。

app/main/view.py:

# from ...
@main.route('/', methods=['GET', 'POST'])
def index():
    form = NameForm()
    if form.validate_on_submit():
         #...
        return redirect(url_for('.index'))
    return render_template('index.html',form=form, name=session.get('name'),known=session.get('known', False))
1)不再像单文件@route('/'),有了蓝本 以后,路由都是由蓝本提供,所以前面要加上蓝本的名字
@main.route('/', methods=['GET', 'POST'])。
2)url_for 不再像单文件的url_for('index'),现在需要指明相应的蓝本url_for('main.index')
url_for实际上就是根据后面的参数来生成url的,以便程序跳转,所以一般都这样用redirect(url_for('.index')) 
3)蓝本的端点都是有命名空间的,其实就是蓝本的名字,这样可以避免冲突。例如main.index和auth.index就是两个页面,可以同时存在。
4)url_for('main.index')可以简写成url_for('.index'),这里省略的命名空间就是当前请求所在的蓝本,例如现在的main,跨蓝本的重定向必须带有命名空间的端点名。

7.5 需求文件

(venv) $ pip freeze >requirements.txt     //生成需求文件
(venv) $ pip install -r requirements.txt    //使用需求文件去安装各种包
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,026评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,655评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,726评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,204评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,558评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,731评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,944评论 2 314
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,698评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,438评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,633评论 2 247
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,125评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,444评论 3 255
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,137评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,103评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,888评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,772评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,669评论 2 271

推荐阅读更多精彩内容