Flask Web开发笔记1-4

0. 前言

git客户端下载:http://git-scm.com/

git clone https://github.com/miguelgrinberg/flasky.git

1、把改动的文件还原成最初的状态

git reset --hard

2、git fetch命令根据GitHub上远程仓库更新本地仓库的提交历史和标签,但不会真正改动源文件。

git fetch --all
git fetch --tags

1. 安装

Flask有3个主要依赖:路由、调试和Web服务器网关接口(WSGI,Web server gateway interface)子系统由Werkzeug提供;模板系统由Jinja2提供;命令行集成由Click提供。

1.1 创建应用目录

git clone https://github.com/miguelgrinberg/flasky.git
cd flasky
git checkout 1a
# 1a代表一个标签(tag),是项目中某次提交历史的名称。

虚拟环境感觉在linux下比较合适,windows下用不上。

2. 应用的基本结构

所有Flask应用都必须创建一个应用实例。Web服务器使用一种名为Web服务器网关接口(WSGI,Web server gateway interface,读作"wiz-ghee")的协议,把接收自客户端的所有请求都转交给这个对象处理。

2.2 路由和视图函数

@app.route('/user/<name>')
def user(name):
	return 'hello,{}'.format(name)

指定其他类型:
/user/<int:id>
string,int,float,path,
path类型是一种特殊的字符串,与string类型不同的是,它可以包括正斜线。

2.6 调试模式

重载器和调试器

2.8 请求-响应循环

2.8.1 应用和请求上下文

变量名上下文说明
current_app应用上下文当前应用的应用实例
g应用上下文处理请求时用作临时存储的对象,每次请求都会重设这个变量
request请求上下文请求对象, 封装了客户端发出的HTTP请求中的内容
session请求上下文用户对话,值为一个字典,存储请求之间需要"记住"的值
>>> from hello import app
>>> from flask import current_app
>>> app_ctx = app.app_context()
>>> app_ctx.push()
>>> current_app.name
'hello'
>>> app_ctx.pop()

获取应用上下文的方法是在应用实例上调用app.app_context()

2.8.2 请求分派

>>> app.url_map
Map([<Rule '/static/<filename>' (OPTIONS, GET, HEAD) -> static>])

2,8.3 请求对象

request.values

属性或方法说明
form一个字典,存储请求提交的所有表单字段
args一个字典,存储通过URL查询字符串传递的所有参数
values一个字典,form和args的合集
cookies一个字典,存储请求的所有cookie
headers一个字典,存储请求的所有HTTP首部
files一个字典,存储请求上传的所有文件
get_data()返回请求主体缓冲的数据
get_json()返回一个Python字典,包含解析请求主体后得到的json
blueprint处理请求的Flask蓝本的名称
endpoint处理请求的Flask端点的名称;Flask把视图函数的名称用作路由端点的名称
methodHTTP请求方法,例如GET或POST
schemeURL方案(http或https)
is_secure()通过安全的连接(HTTPS)发送请求时返回True
host请求定义的主机名,如果客户端定义了端口号,还包括端口号
pathURL的路径部分
query_stringURL的查询字符串部分,返回原始二进制值
full_pathURL的路径和查询字符串部分
url客户端请求的完整URL
base_url同url,但没有查询字符串部分
remote_addr客户端的IP地址
environ请求的原始WSGI环境字典

2.8.4 请求钩子

请求钩子通过装饰器实现。Flask支持以下4种钩子。

钩子说明
before_request注册一个函数,在每次请求之前运行。
before_first_request注册一个函数,只在处理第一个请求之前运行。可以通过这个钩子添加服务器初始化任务。
after_request注册一个函数,如果没有未处理的异常抛出,在每次请求之后运行。
teardown_request注册一个函数,即使有未处理的异常抛出,也在每次请求之后运行。

在请求钩子函数和视图函数之间共享数据一般使用上下文全局变量g。例如,before_request处理程序可以从数据库中加载已登录用户,并将其保存到g.user中。随后调用视图函数时,便可以通过g.user获取用户。

2.8.5 响应

方式一:return

数字代码作为第二个返回值,添加到响应文本之后

return '<h1>Bad Request</h1>',400

方式二:响应对象

from flask import make_response
response = make_reponse('<h1>flask</h1>')
response.set_cookie('answer','12')

响应对象最常使用的属性和方法如下所示:

属性或方法说明
status_codeHTTP数字状态码
headers一个类似字典的对象,包含随响应发送的所有首部
set_cookie()为响应添加一个cookie
delete_cookie()删除一个cookie
content_length响应主体的长度
content_type响应主体的媒体类型
set_data()使用字符串或字节值设定响应
get_data()获取响应主体

方式三:重定向

from flask import redirect
return redirect('http://baidu.com')

方式四:abort

from flask import abort
abort(404)

3. 模板

为了渲染模板,Flask使用Jinja2的模板引擎。

3.1 Jinja2模板引擎

3.1.2 变量

Jinja2能识别所有类型的变量,甚至是一些复杂的类型,例如字典、列表和对象。

<p>a value from a dictionary: {{ mydict['key'] }} </p>
<p>a value from a list: {{ mylist[3] }}</p>
<p>a value from a list, with a variable index: {{ mylist[myintvar] }}</p>
<p>a value from an object's method: {{ myobj.somemethod() }}</p>

变量值可以使用过滤器修改,过滤器加在变量名之后,二者之间以竖线分隔。

hello, {{ name|capitalize }}

Jinja2变量过滤器

过滤器名说明
safe渲染值时不转义
capitalize把值的首字母转换成大写,其他字母转换成小写
lower把值转换成小写形式
upper把值转换成大写形式
title把值中每个单词的首字母都转换成大写
trim把值的首尾空格删掉
striptags渲染之前把值中所有的HTML标签都删掉

3.1.3 控制结构

条件判断语句:

{% if user %}
	hello,{{ user }}!
{% else %}
	hello,stranger!
{% endif %}

循环:

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

宏:略

模板继承:base.html

<html>
<head>
	{% block head %}
	<title>{% block title %}{% endblock %} - my application </title>
	{% endblock %}
</head>
<body>
	{% block body %}
	{% endblock %}
</body>
</html>
{% extends "base.html" %}

3.2 使用Flask-Bootstrap集成Bootstrap(!)

bootstrap4 中文开发手册:https://www.php.cn/manual/view/34100.html

git checkout 3b

.navbar-inverse 表示反色的导航栏,是一个带有黑色背景白色文本的反色的导航栏。

.navbar-toggle来修饰button表示这是一个控制切换状态的button。

.collapse.navbar-collapse通过父断点来分组和隐藏导航栏内容。

icon-bar当屏幕宽度小于一定程度时,导航条缩小,变成可折叠形式

3.3 自定义错误页面

hello.py

from flask import Flask, render_template
from flask_bootstrap import Bootstrap

app = Flask(__name__)

bootstrap = Bootstrap(app)


@app.errorhandler(404)
def page_not_found(e):
    return render_template('404.html'), 404


@app.errorhandler(500)
def internal_server_error(e):
    return render_template('500.html'), 500


@app.route('/')
def index():
    return render_template('index.html')


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

3.3.1 base.html

templates/base.html

{% extends "bootstrap/base.html" %}

{% block title %}Flasky{% endblock %}

{% block navbar %}
<div class="navbar navbar-inverse" role="navigation">
    <div class="container">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="/">Flasky</a>
        </div>
        <div class="navbar-collapse collapse">
            <ul class="nav navbar-nav">
                <li><a href="/">Home</a></li>
            </ul>
        </div>
    </div>
</div>
{% endblock %}

{% block content %}
<div class="container">
    {% block page_content %}{% endblock %}
</div>
{% endblock %}

3.3.2 404

{% extends "base.html" %}

{% block title %}Flasky - Page Not Found{% endblock %}

{% block page_content %}
<div class="page-header">
    <h1>Not Found</h1>
</div>
{% endblock %}

在这里插入图片描述

3.4 链接(url_for)

Flask提供url_for()辅助函数,它使用应用的URL映射中保存的信息生成URL。

url_for()函数的第一个且唯一必须指定的参数是端点名,即路由的内部名称。默认情况下,路由的端点是相应视图函数的名称。

url_for()函数最简单的用法是以视图函数名作为参数,返回对应的url

{{ url_for('static', filename='favicon.ico') }}

对于3.3的hello.py而言,

url_for('index') 得到的结果是/

url_for('index',_external = True) 返回的是绝对地址

使用url_for()生成动态url时,将动态部分作为关键字参数传入。例如:
url_for('user',name = 'john',_external = True) 返回结果http://localhost:5000/user/john

非动态的参数也可以添加到查询字符串。
url_for('user',name = 'john',page = 2,version = 1)
返回结果/user/john?page=2&version=1

3.5 静态文件

templates/base.html

插入head区块的末尾

{% block head %}
{{ super() }}
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">
{% endblock %}

为了保留基模板中这个区块里的原始内容,调用了super函数。

3.6 使用Flask-Moment本地化日期和时间

hello.py

#coding:utf-8
from datetime import datetime
from flask import Flask, render_template
from flask_bootstrap import Bootstrap
from flask_moment import Moment

app = Flask(__name__)

bootstrap = Bootstrap(app)
moment = Moment(app)

@app.route('/')
def index():
    return render_template('index.html',current_time=datetime.utcnow())
# 协调世界时间(UTC,coordinated universal time)

if __name__ == '__main__':
    app.run('127.0.0.1',port = '10110')

index.html

format(‘LLL’)函数根据客户端计算机中的时区和区域设置渲染日期和时间。参数决定了渲染的方式,从’L’到’LLLL’分别对应不同的复杂度。

fromNow()渲染相对时间戳,而且会随着时间的推移自动刷新显示的时间(refresh=True)。

{% extends "base.html" %}

{% block title %}Flasky{% endblock %}

{% block page_content %}
<div class="page-header">
    <h1>Hello World!</h1>
</div>
<p>The local date and time is {{ moment(current_time).format('LLL') }}.</p>
<p>That was {{ moment(current_time).fromNow(refresh=True) }}.</p>
{% endblock %}

base.html

{% extends "bootstrap/base.html" %}

{% block title %}Flasky{% endblock %}

{% block head %}
{{ super() }}
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">
{% endblock %}

{% block navbar %}
<div class="navbar navbar-inverse" role="navigation">
    <div class="container">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="/">Flasky</a>
        </div>
        <div class="navbar-collapse collapse">
            <ul class="nav navbar-nav">
                <li><a href="/">Home</a></li>
            </ul>
        </div>
    </div>
</div>
{% endblock %}

{% block content %}
<div class="container">
    {% block page_content %}{% endblock %}
</div>
{% endblock %}

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

在这里插入图片描述

4. Web表单

Flask-WTF扩展用于处理web表单。

from flask import Flask
app = Flask(__name__)
app.config['SECRET_KEY'] = 'hard to guess string'

Flask-WTF之所以要求应用配置一个密钥,是为了防止表单遭到跨站请求伪造(CSRF,cross-site request forgery)攻击。恶意网站把请求发送到被攻击者已登录的其他网站时,就会引发CSRF攻击。Flask-WTF为所有表单生成安全令牌,存储在用户会话中。令牌是一种加密签名,根据密钥生成。

4.2 表单类

#coding:utf-8
from flask_wtf import FlaskForm
from wtforms import StringField,SubmitField
from wtforms.validators import DataRequired

class NameForm(FlaskForm):
    name = StringField('What is your name?',validators = [DataRequired()])
    # StringField类表示属性为type="text"的HTML <input>元素
    submit = SubmitField('Submit')
    # SubmitField类表示属性为type="submit"的HTML <input>元素

WTForms支持的HTML标准字段

WTForms内建的验证函数

4.3 把表单渲染成HTML

Flask-Bootstrap扩展提供了一个高层级的辅助函数,可以使用Bootstrap预定义的表单样式渲染整个Flask-WTF表单,而这些操作只需一次调用即可完成。

import导入模板元素,在多个模板中使用。导入的bootstrap/wtf.html文件中定义了一个使用Bootstrap渲染Flask-WTF表单对象的辅助函数。wtf.quick_form()函数的参数为Flask-WTF表单对象,使用Bootstrap的默认样式渲染传入的表单。

index.html(截取部分,完整请查看4a)

{% import "bootstrap/wtf.html" as wtf %}
{{ wtf.quick_form(form) }}

hello.py(截取部分,完整请查看4a)

from flask import Flask, render_template
from flask_bootstrap import Bootstrap
from flask_moment import Moment
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired

app = Flask(__name__)
app.config['SECRET_KEY'] = 'hard to guess string'

bootstrap = Bootstrap(app)

class NameForm(FlaskForm):
    name = StringField('What is your name?', validators=[DataRequired()])
    submit = SubmitField('Submit')

@app.route('/', methods=['GET', 'POST'])
def index():
    name = None
    form = NameForm()
    if form.validate_on_submit():
        name = form.name.data
        form.name.data = ''
    return render_template('index.html', form=form, name=name)

4.5 重定向和用户会话

git checkout 4b

@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'))

4.6 闪现消息

hello.py

@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">&times;</button>
        {{ message }}
    </div>
    {% endfor %}

    {% block page_content %}{% endblock %}
</div>
{% endblock %}

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">&times;</button>
        {{ message }}
    </div>
    {% endfor %}

    {% block page_content %}{% endblock %}
</div>
{% endblock %}

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值