1.web框架?
协助开发者快速开发应用程序的一套功能代码.开发者只需要按照框架约定要求,在指定位置写自己的业务逻辑代码.
内部逻辑后续交流中会使用代码的方式进行互相交流学习。
2.为什么用web框架?
稳定性和可扩展性强,提高开发效率,减少开发难度,使开发简便.常用web框架有:flask和Django.
Flask:Flask 本身相当于一个内核,其他几乎所有的功能都要用到扩展(邮件扩展Flask-Mail,用户认证Flask-Login,数据库Flask-SQLAlchemy),都需要用第三方的扩展来实现。其 WSGI 工具箱采用 Werkzeug(路由模块),模板引擎则使用 Jinja2。这两个也是 Flask 框架的核心。
3.创建pyton项目:hello world
1.导入Flask类:
from flask import Flask
2.创建程序实例:
app = Flask(__name__) // 它指向程序所在的包
3.装饰路由:
@app.route('/')
def demo():
return "hello world" //装饰器的作用是将路由映射到视图函数demo,通俗的说就是URL的绑定
4.启动web服务器
if __name__ == "__main__":
app.run() //在程序运行过程中,程序实例中会使用url_map将装饰器路由和视图函数的对应关系保存起来.之前给你简单的点击过源码。不是重点可以忽略!
4.相关配置参数:
4.1.Flask 程序初始化参数:
import__name__ //Flask程序所在的包(模块),传__name__即可,其可决定Flask文件在访问静态文件时查找的路径.
static_url_path //静态文件访问路径,可以不传,默认为/static_folder.
static_folder //静态文件存储的文件夹,可以不传,默认为static.注意此处没有加s,命名规范
template_folder //模板文件存储的文件夹,可以不传,默认为templates.
4.2.Flask 程序相关配置加载方式:
4.2.1配置对象.从配置对象中加载,创建配置的类,代码如下:.
# 配置对象,里面定义需要给 APP 添加的一系列配置
class Config(object):
DEBUG = True
# 创建 Flask 类的对象,指向程序所在的包的名称
app = Flask(__name__)
# 从配置对象中加载配置
app.config.from_object(Config)
4.2.2配置文件
创建配置文件
config.ini
,在配置文件中添加配置 DEBUG = True
# 创建 Flask 类的对象,指向程序所在的包的名称
app = Flask(__name__)
# 从配置文件中加载配置
app.config.from_pyfile('config.ini')
4.2.3环境变量.
编辑运行的相关配置
# 创建 Flask 类的对象,指向程序所在的包的名称
app = Flask(__name__)
# 加载指定环境变量名称所对应的相关配置
app.config.from_envvar('FLASKCONFIG') //name值为FLASKCONFIG,value值为config.ini
4.2.4直接app.的方式设置
app.debug = True
app.config['DEBUG'] = True
4.3.app.run() 参数:
可以指定运行的主机IP地址,端口,是否开启调试模式
app.run(host="0.0.0.0", port=5000, debug = True)
5.路由基本定义:
路由:客户端把请求发送给Web服务器,Web服务器再把请求发送给程序实例。程序实例需要知道对每个URL请求运行哪些代码,所以保存了一个 URL 到 Python 函数的映射关系。处理URL和函数之间关系的程序称路由。
在Flask定义路由的最简便方式,是使用程序实例提供的app.route装饰器,把装饰的函数注册为路由.
路由的本质路由的本质,是URL 绑定, @app.route() 装饰器用于把一个函数绑于一个URL上,如上,/绑定了index()函数,/user绑定了hello_user()函数,这个例子将 index() 函数注册为程序根 '/' 地址。访问 http://localhost:5000/ 后,会触发服务器执行 index() 函数,这个函数的返回值称为响应,是客户端接收到的内容。
像 index() 这样的函数称为视图函数。视图函数返回的响应可以是包含 HTML 的简单字符串,也可以是复杂的表单
路由查找方式
视图函数作用: 处理业务逻辑和返回响应内容;
同一路由指向两个不同的函数,在匹配过程中,至上而下依次匹配
from flask import Flask
app = Flask(name)
@app.route('/')
def hello():
return '<h1>hello world</h1>'
@app.route('/')
def hello_2017():
return '<h1>hello 2017</h1>'
if __name__ == "__main__"
app.run()
所以上面路由 / 输出的结果为 hello 函数的结果
1.指定路由地址
# 指定访问路径为 demo1
@app.route('/demo1')
def demo1():
return 'demo1'
2.路由传递的参数默认当做string处理.
# 路由传递参数 //有时我们需要将同一类 URL 映射到同一个视图函数处理;
@app.route('/user/<user_id>')
def user_info(user_id):
return 'hello %s' % user_id
3.路由传递的参数也可以指定参数的类型.
# 路由传递参数
@app.route('/user/<int:user_id>')
def user_info(user_id):
return 'hello %d' % user_id
//这里指定int,尖括号中的内容是动态的,在此暂时可以理解为接受 int 类型的值,实际上 int 代表使用 IntegerConverter去处理 url 传入的参数;
4.请求方式 //Flask中默认的请求方式为"get", 自带OPTIONS和HEAD
@app.route('/demo2', methods=['GET', 'POST'])
def demo2():
# 直接从请求中取到请求方式并返回
return request.method
6.视图常用逻辑
6.1返回 JSON
使用 Flask 写一个接口时候需要给客户端返回 JSON 数据,在 Flask 中可以直接使用 jsonify 生成一个 JSON 的响应
//返回json
@app.route('/demo4')
def demo4():
json_dict = {
"user_id": 10,
"user_name": "laowang"
} //json.dumps一般是text/html格式,jsonify做了封装指定响应内容.
return jsonify(json_dict) //jsonify会指定响应内容的数据格式(告诉客户端我返回给你的数据格式是什么)
6.2重定向
# 重定向 (重定向到黑马官网)
@app.route('/demo5')
def demo5():
return redirect('http://www.zhanghaibin.com')
//重定向到自己写的视图函数
//可以直接填写自己 url 路径
//也可以使用 url_for 生成指定视图函数所对应的 url
@app.route('/demo1')
def demo1():
return 'demo1'
# 重定向
@app.route('/demo5')
def demo5():
return redirect(url_for('demo1'))
//重定向到带有参数的视图函数
//在 url_for 函数中传入参数
# 路由传递参数
@app.route('/user/<int:user_id>')
def user_info(user_id):
return 'hello %d' % user_id
# 重定向
@app.route('/demo5') //url_for:取到指定视图函数所对应的路由URL,并且可以携带参数
def demo5():
# 使用 url_for 生成指定视图函数所对应的 url
return redirect(url_for('user_info', user_id=100))
6.3自定义状态码
//在 Flask 中,可以很方便的返回自定义状态码,以实现不符合 http 协议的状态码,例如:status code: 666
@app.route('/demo6')
def demo6():
return '状态码为 666', 666
1.正则匹配路由
在 web 开发中,可能会出现限制用户访问规则的场景,那么这个时候就需要用到正则匹配,根据自己的规则去限定请求参数再进行访问 ;
具体实现步骤为:
1.1导入转换器基类:在 Flask 中,所有的路由的匹配规则都是使用转换器对象进行记录
1.2自定义转换器:自定义类继承于转换器基类
1.3添加转换器到默认的转换器字典中
1.4使用自定义转换器实现自定义匹配规则
1.1代码实现
1.1导入转换器基类
from werkzeug.routing import BaseConverter
自定义转换器
1.2自定义正则转换器
class RegexConverter(BaseConverter):
def __init__(self, url_map, *args):
super(RegexConverter, self).__init__(url_map)
# 将接受的第1个参数当作匹配规则进行保存
self.regex = args[0]
添加转换器到默认的转换器字典中,并指定转换器使用时名字为: re
app = Flask(__name__)
1.3将自定义转换器添加到转换器字典中,并指定转换器使用时名字为: re
app.url_map.converters['re'] = RegexConverter
1.4使用转换器去实现自定义匹配规则
当前此处定义的规则是:3位数字
@app.route('/user/<re("[0-9]{3}"):user_id>')
def user_info(user_id):
return "user_id 为 %s" % user_id
1.2自定义转换器其他两个函数实现
继承于自定义转换器之后,还可以实现 to_python 和 to_url 这两个函数去对匹配参数做进一步处理 :
to_python:
-
该函数参数中的 value 值代表匹配到的值,可输出进行查看
-
匹配完成之后,对匹配到的参数作最后一步处理再返回
to_url:
-
在使用 url_for 去获取视图函数所对应的 url 的时候,会调用此方法对 url_for 后面传入的视图函数参数做进一步处理
from flask import Flask
from flask import redirect
from flask import url_for
from werkzeug.routing import BaseConverter
class RegexConverter(BaseConverter):
"""自定义正则的转换器"""
# regex = "[0-9]{6}"
def __init__(self, url_map, *args):
super(RegexConverter, self).__init__(url_map)
# 取到第1个参数,给regex属性赋值
self.regex = args[0]
class ListConverter(BaseConverter):
"""自己定义转换器"""
regex = "(\\d+,?)+\\d$" //匹配到的是列表字符串1,2,3,4
def to_python(self, value):
"""当匹配到参数之后,对参数做进一步处理之后,再返回给视图函数中"""
return value.split(',')
def to_url(self, value):
"""使用url_for的时候,对视图函数传的参数进行处理,处理完毕之后以便能够进行路由匹配"""
result = ','.join(str(v) for v in value)
return result
app = Flask(__name__)
# 将自己的转换器添加到默认的转换器列表中
app.url_map.converters["re"] = RegexConverter
app.url_map.converters["list"] = ListConverter
@app.route('/')
def index():
return 'index'
# 规则:/user/6位数字 [0-9]{6}
# 自定义转换器
@app.route('/user/<re("[0-9]{6}"):user_id>')
def demo1(user_id):
return '用户id是 %s' % user_id
@app.route('/users/<list:user_ids>')
def demo2(user_ids):
# 如果才能在视图函数中接收到的 user_ids 就是一个列表
return "用户的id列表是 %s" % user_ids
@app.route('/demo3')
def demo3():
return redirect(url_for('demo2', user_ids=[1, 3, 4, 5]))
if __name__ == '__main__':
app.run(debug=True)
系统自带转换器共六种:
DEFAULT_CONVERTERS = {
'default': UnicodeConverter,
'string': UnicodeConverter,
'any': AnyConverter,
'path': PathConverter,
'int': IntegerConverter,
'float': FloatConverter,
'uuid': UUIDConverter,
}
2.异常捕获
2.1HTTP 异常主动抛出
abort 方法 : abort(500) 注意:抛出状态码的话,只能抛出 HTTP 协议的错误状态码 .
2.2捕获错误
errorhandler 装饰器
-
注册一个错误处理程序,当程序抛出指定错误状态码的时候,就会调用该装饰器所装饰的方法
-
参数:
-
code_or_exception – HTTP的错误状态码或指定异常
-
@app.errorhandler(500)
def internal_server_error(e):
return '服务器搬家了'
3.请求勾子
在请求开始时,建立数据库连接 ;
在请求开始时,根据需求进行权限校验;
在请求结束时,指定数据的交互格式;
为了让每个视图函数避免编写重复功能的代码,Flask提供了通用设施的功能,即请求钩子 .
请求钩子是通过装饰器的形式实现,Flask支持如下四种请求钩子 :
from flask import Flask
from flask import abort
app = Flask(__name__)
# 在第一次请求之前调用,可以在此方法内部做一些初始化操作
@app.before_first_request
def before_first_request():
"""在第一次请求之前会访问该函数"""
print("before_first_request")
# 在每一次请求之前调用,这时候已经有请求了,可能在这个方法里面做请求的校验
# 如果请求的校验不成功,可以直接在此方法中进行响应,直接return之后那么就不会执行视图函数
@app.before_request
def before_request():
"""在每次请求之前都会调用"""
print("before_request")
# if 请求不符合条件:
# return "laowang"
# 在执行完视图函数之后会调用,并且会把视图函数所生成的响应传入,可以在此方法中对响应做最后一步统一的处理
@app.after_request
def after_request(response):
"""在请求之后会调用,并且函数里面接受一个参数:响应,还需要将响应进行返回"""
print("after_request")
response.headers["Content-Type"] = "application/json"
return response
# 请每一次请求之后都会调用,会接受一个参数,参数是服务器出现的错误信息
@app.teardown_request
def teardown_request(error):
"""在请求之后会执行,如果请求的函数报有异常,会把具体异常传入到此函数"""
print("teardown_request")
@app.route('/')
def index():
return 'index'
if __name__ == '__main__':
app.run(debug=True)
4.装饰器路由具体实现梳理
Flask有两大核心:Werkzeug和Jinja2
4.1.Werkzeug
Werkzeug实现路由、调试和Web服务器网关接口
Werkzeug是一个遵循WSGI协议的python函数库
- 其内部实现了很多Web框架底层的东西,比如request和response对象;
- 与WSGI规范的兼容;支持Unicode;
- 支持基本的会话管理和签名Cookie;
- 集成URL请求路由等。
Werkzeug库的 routing 模块负责实现 URL 解析。不同的 URL 对应不同的视图函数,routing模块会对请求信息的URL进行解析,匹配到URL对应的视图函数,执行该函数以此生成一个响应信息。
routing模块内部有:
Rule类
用来构造不同的URL模式的对象,路由URL规则
Map类
存储所有的URL规则和一些配置参数
BaseConverter的子类
负责定义匹配规则
MapAdapter类
负责协调Rule做具体的匹配的工作
4.2.Jinja2实现了模板
5.获取请求参数
request 就是flask中代表当前请求的 request 对象,其中一个请求上下文变量(理解成全局变量,在视图函数中直接使用可以取到当前本次请求)
常用的属性如下:
属性 | 说明 | 类型 |
---|---|---|
data | 记录请求的数据,并转换为字符串 | * |
form | 记录请求中的表单数据 | MultiDict |
args | 记录请求中的查询参数 | MultiDict |
cookies | 记录请求中的cookie信息 | Dict |
headers | 记录请求中的报文头 | EnvironHeaders |
method | 记录请求使用的HTTP方法 | GET/POST |
url | 记录请求的URL地址 | string |
files | 记录请求上传的文件 | * |
代码实现
//get请求用args接
//post请求用form接
//原始数据用data属性,post表单用form属性,files文件必须用post中的form-data,raw没有格式限制.
from flask import Flask
from flask import request
app = Flask(__name__)
@app.route('/')
def index():
return 'index'
@app.route('/args')
def args():
username = request.args.get("username")
password = request.args.get("password")
print("%s %s" % (username, password))
return "get请求是OK"
@app.route('/form', methods=['POST'])
def form():
username = request.form.get("username")
password = request.form.get("password")
print("%s %s" % (username, password))
return "post表单请求用form接收OK"
@app.route('/upload', methods=['POST'])
def upload():
file = request.files.get('pic')
file.save('./static/aaa.png')
return 'success'
@app.route('/data', methods=['POST'])
def data():
data = request.data
print(data)
return 'data返回的是json格式的数据'
if __name__ == '__main__':
app.run(debug=True) //开启自动调试模式debug = True
6.状态保持
为什么要状态保持? 因为http是一种无状态的协议,浏览器请求服务器是无状态的 .
状态保持的目的是在一段时间内跟踪请求者的状态,可以实现跨页面访问当前请求者的数据.
无状态协议:
-
协议对于事务处理没有记忆能力
-
对同一个 url 请求没有上下文关系
-
每次的请求都是独立的,它的执行情况和结果与前面的请求和之后的请求是无直接关系的,它不会受前面的请求应答情况直接影响,也不会直接影响后面的请求应答情况
-
服务器中没有保存客户端的状态,客户端必须每次带上自己的状态去请求服务器
-
人生若只如初见
无状态:指一次用户请求时,浏览器、服务器无法知道之前这个用户做过什么,每次请求都是一次新的请求。
无状态原因:浏览器与服务器是使用 socket 套接字进行通信的,服务器将请求结果返回给浏览器之后,会关闭当前的 socket 连接,而且服务器也会在处理页面完毕之后销毁页面对象。
需求: 有时需要保持下来用户浏览的状态,比如用户是否登录过,浏览过哪些商品等;
实现状态保持主要有两种方式:
-
在客户端存储信息使用
Cookie
-
在服务器端存储信息使用
Session
cookie 和 session的区别:需要你进行资料的整理和查询
之前给你强调过的两种技术
上下文:相当于一个容器,保存了 Flask 程序运行过程中的一些信息。
Flask中有两种上下文,请求上下文和应用上下文:分别包含哪几种,作用,这点很重要,理解csrf会用到。
7.Flaks-script扩展
命令行操作(自行了解即可)
8.模板
视图函数的两个作用: 处理业务逻辑和返回响应内容;
模板,它的作用即承担视图函数的另一个作用,即返回响应内容。
模板其实是一个包含响应文本的文件,其中用占位符(变量)表示动态部分,告诉模板引擎其具体的值需要从使用的数据中获取;
使用真实值替换变量,再返回最终得到的字符串,这个过程称为“渲染";
Flask是使用 Jinja2 这个模板引擎来渲染模板
使用模板的优点: 视图函数只负责业务逻辑和数据处理(业务逻辑方面)
**而模板则将取到视图函数的数据结果进行展示(视图展示方面)**
**代码结构清晰,耦合度低**
8.1jinjia2
Jinja2:是 Python 下一个被广泛应用的模板引擎,是由Python实现的模板语言,他的设计思想来源于 Django 的模板引擎,并扩展了其语法和一系列强大的功能,其是Flask内置的模板语言。模板语言:是一种被设计来自动生成文档的简单文本格式,在模板语言中,一般都会把一些变量传给模板,替换模板的特定位置上预先定义好的占位变量名。
{{ }} 来表示变量名,这种 {{}} 语法叫做变量代码块
Jinja2 模版中的变量代码块可以是任意 Python 类型或者对象,只要它能够被 Python 的 str() 方法转换为一个字符串就可以;
用 {% %} 定义的控制代码块,可以实现一些语言层次的功能,比如循环或者if语句
渲染模版函数Flask提供的 render_template 函数封装了该模板引擎render_template 函数的第一个参数是模板的文件名,后面的参数都是键值对,表示模板中变量对应的真实值。
@app.route('/')
def index():
my_list = [
{
"id": 1,
"value": "我爱工作"
},
{
"id": 2,
"value": "工作使人快乐"
},
{
"id": 3,
"value": "沉迷于工作无法自拔"
},
{
"id": 4,
"value": "日渐消瘦"
},
{
"id": 5,
"value": "张海斌,爱学习"
}
]
return render_template('control.html',
my_list_dict=my_list) //注意模板的文件名,还有接收数据的变量名
-
模板中代码
<body>
{% for item in my_list_dict if item.id != 5 %}
{% if loop.index == 1 %}
<li style="{{ item.value }}</li>
{% elif loop.index == 2 %}
<li style="{{ item.value }}</li>
{% elif loop.index == 3 %}
<li style="{{ item.value }}</li>
{% else %}
<li style="{{ item.value }}</li>
{% endif %} //if判断必须有endif结尾
{% endfor %} //for循环必须有endfor结尾
</body>
8.2模板的使用
1.在项目下创建 templates 文件夹,用于存放所有的模板文件,并在目录下创建一个模板html文件temp_demo1.html
2.设置 templates 文件夹属性以便能够在代码中有智能提示
3.设置 html 中的模板语言,以便在 html 有智能提示
4.创建视图函数,将该模板内容进行渲染返回
5.代码中传入字符串,列表,字典到模板中
8.3过滤器
过滤器的本质就是函数。有时候我们不仅仅只是需要输出变量的值,我们还需要修改变量的显示,甚至格式化、运算等等,而在模板中是不能直接调用 Python 中的某些方法,那么这就用到了过滤器。
8.3.1过滤器的使用方式
过滤器的使用方式为:变量名 | 过滤器。
在 jinja2 中,过滤器是可以支持链式调用的
{{ "hello world" | reverse | upper }}
{{'haibin' | reverse | upper }}<br>
{{ 'haibin' | reverse }}<br>
{{ my_str }}<br>
默认是安全的,
{{ my_str | escape }}<br>
//加safe后会解析js代码,不加safe不会解析,会直接显示传入的js字符串脚本
{{ my_str | safe }}<br>
8.3.2常见内建过滤器
safe:禁用转义
capitalize:把变量值的首字母转成大写,其余字母转小写
lower:把值转成小写
upper:把值转成大写
title:把值中的每个单词的首字母都转成大写
reverse:字符串反转
8.4自定义过滤器
过滤器的本质是函数。当模板内置的过滤器不能满足需求,可以自定义过滤器。自定义过滤器有两种实现方式:
-
一种是通过Flask应用对象的 add_template_filter 方法
-
通过装饰器来实现自定义过滤器
重要:自定义的过滤器名称如果和内置的过滤器重名,会覆盖内置的过滤器。
8.4.1方式一
通过调用应用程序实例的 add_template_filter 方法实现自定义过滤器。该方法第一个参数是函数名,第二个参数是自定义的过滤器名称:
def do_listreverse(li):
# 通过原列表创建一个新列表
temp_li = list(li) //这样不会对原来的列表进行修改
# 将新列表进行返转
temp_li.reverse()
return temp_li
app.add_template_filter(do_listreverse,'lireverse')
8.4.2方式二
用装饰器来实现自定义过滤器。装饰器传入的参数是自定义的过滤器名称。
@app.template_filter('lireverse')
def do_listreverse(li):
# 通过原列表创建一个新列表
temp_li = list(li)
# 将新列表进行返转
temp_li.reverse()
return temp_li
8.5模板代码的复用
对宏(macro)的理解:
把它看作 Jinja2 中的一个函数,它会返回一个模板或者 HTML 字符串为了避免反复地编写同样的模板代码,出现代码冗余,可以把他们写成函数以进行重用需要在多处重复使用的模板代码片段可以写入单独的文件,再包含在所有模板中,以避免重复
对模板继承(block)的理解:
模板继承是为了重用模板中的公共内容。一般Web开发中,继承主要使用在网站的顶部菜单、底部。这些内容可以定义在父模板中,子模板直接继承,而不需要重复书写。
对包含(include)的理解:
Jinja2模板中,除了宏和继承,还支持一种代码重用的功能,叫包含(Include)。它的功能是将另一个模板整个加载到当前模板中,并直接渲染。包含在使用时,如果包含的模板文件不存在时,程序会抛出TemplateNotFound异常,可以加上 ignore missing 关键字。如果包含的模板文件不存在,会忽略这条include语句。
后续进行后面的内容:
orm
数据库的迁移操作
重点内容均在后续,前面内容需要搞明白.....
# 以上均为刘亚杰帮忙整理,在此表示感谢。
2019-07-03 07:44:28