协助开发者快速开发应用程序的一套功能代码.开发者只须要按照框架约定要求,在指定位置写本身的业务逻辑代码.html
内部逻辑后续交流中会使用代码的方式进行互相交流学习。python
稳定性和可扩展性强,提升开发效率,减小开发难度,使开发简便.经常使用web框架有:flask和Django.web
Flask:Flask 自己至关于一个内核,其余几乎全部的功能都要用到扩展(邮件扩展Flask-Mail,用户认证Flask-Login,数据库Flask-SQLAlchemy),都须要用第三方的扩展来实现。其 WSGI 工具箱采用 Werkzeug(路由模块),模板引擎则使用 Jinja2。这两个也是 Flask 框架的核心。数据库
1.导入Flask类:json
from flask import Flask
2.建立程序实例:flask
app = Flask(__name__) // 它指向程序所在的包
3.装饰路由:api
@app.route('/')
def demo():
return "hello world" //装饰器的做用是将路由映射到视图函数demo,通俗的说就是URL的绑定
4.启动web服务器浏览器
if __name__ == "__main__":
app.run() //在程序运行过程当中,程序实例中会使用url_map将装饰器路由和视图函数的对应关系保存起来.以前给你简单的点击过源码。不是重点能够忽略!
import__name__ //Flask程序所在的包(模块),传__name__便可,其可决定Flask文件在访问静态文件时查找的路径.
static_url_path //静态文件访问路径,能够不传,默认为/static_folder.
static_folder //静态文件存储的文件夹,能够不传,默认为static.注意此处没有加s,命名规范
template_folder //模板文件存储的文件夹,能够不传,默认为templates.
# 配置对象,里面定义须要给 APP 添加的一系列配置
class Config(object):
DEBUG = True
# 建立 Flask 类的对象,指向程序所在的包的名称
app = Flask(__name__)
# 从配置对象中加载配置
app.config.from_object(Config)
建立配置文件 config.ini
,在配置文件中添加配置 DEBUG = True安全
# 建立 Flask 类的对象,指向程序所在的包的名称
app = Flask(__name__)
# 从配置文件中加载配置
app.config.from_pyfile('config.ini')
编辑运行的相关配置服务器
# 建立 Flask 类的对象,指向程序所在的包的名称
app = Flask(__name__)
# 加载指定环境变量名称所对应的相关配置
app.config.from_envvar('FLASKCONFIG') //name值为FLASKCONFIG,value值为config.ini
app.debug = True
app.config['DEBUG'] = True
能够指定运行的主机IP地址,端口,是否开启调试模式
app.run(host="0.0.0.0", port=5000, debug = True)
路由:客户端把请求发送给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
使用 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会指定响应内容的数据格式(告诉客户端我返回给你的数据格式是什么)
# 重定向 (重定向到黑马官网)
@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))
//在 Flask 中,能够很方便的返回自定义状态码,以实现不符合 http 协议的状态码,例如:status code: 666
@app.route('/demo6')
def demo6():
return '状态码为 666', 666
在 web 开发中,可能会出现限制用户访问规则的场景,那么这个时候就须要用到正则匹配,根据本身的规则去限定请求参数再进行访问 ;
具体实现步骤为:
1.1导入转换器基类:在 Flask 中,全部的路由的匹配规则都是使用转换器对象进行记录
1.2自定义转换器:自定义类继承于转换器基类
1.3添加转换器到默认的转换器字典中
1.4使用自定义转换器实现自定义匹配规则
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
继承于自定义转换器以后,还能够实现 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,
}
abort 方法 : abort(500) 注意:抛出状态码的话,只能抛出 HTTP 协议的错误状态码 .
errorhandler 装饰器
注册一个错误处理程序,当程序抛出指定错误状态码的时候,就会调用该装饰器所装饰的方法
参数:
code_or_exception – HTTP的错误状态码或指定异常
@app.errorhandler(500)
def internal_server_error(e):
return '服务器搬家了'
在请求开始时,创建数据库链接 ;
在请求开始时,根据需求进行权限校验;
在请求结束时,指定数据的交互格式;
为了让每一个视图函数避免编写重复功能的代码,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)
Flask有两大核心:Werkzeug和Jinja2
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作具体的匹配的工做
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
为何要状态保持? 由于http是一种无状态的协议,浏览器请求服务器是无状态的 .
状态保持的目的是在一段时间内跟踪请求者的状态,能够实现跨页面访问当前请求者的数据.
无状态协议:
协议对于事务处理没有记忆能力
对同一个 url 请求没有上下文关系
每次的请求都是独立的,它的执行状况和结果与前面的请求和以后的请求是无直接关系的,它不会受前面的请求应答状况直接影响,也不会直接影响后面的请求应答状况
服务器中没有保存客户端的状态,客户端必须每次带上本身的状态去请求服务器
人生若只如初见
无状态:指一次用户请求时,浏览器、服务器没法知道以前这个用户作过什么,每次请求都是一次新的请求。
无状态缘由:浏览器与服务器是使用 socket 套接字进行通讯的,服务器将请求结果返回给浏览器以后,会关闭当前的 socket 链接,并且服务器也会在处理页面完毕以后销毁页面对象。
需求: 有时须要保持下来用户浏览的状态,好比用户是否登陆过,浏览过哪些商品等;
实现状态保持主要有两种方式:
在客户端存储信息使用Cookie
在服务器端存储信息使用Session
cookie 和 session的区别:须要你进行资料的整理和查询
以前给你强调过的两种技术
上下文:至关于一个容器,保存了 Flask 程序运行过程当中的一些信息。
Flask中有两种上下文,请求上下文和应用上下文:分别包含哪几种,做用,这点很重要,理解csrf会用到。
命令行操做(自行了解便可)
视图函数的两个做用: 处理业务逻辑和返回响应内容;
模板,它的做用即承担视图函数的另外一个做用,即返回响应内容。
模板实际上是一个包含响应文本的文件,其中用占位符(变量)表示动态部分,告诉模板引擎其具体的值须要从使用的数据中获取;
使用真实值替换变量,再返回最终获得的字符串,这个过程称为“渲染";
Flask是使用 Jinja2 这个模板引擎来渲染模板
使用模板的优势: 视图函数只负责业务逻辑和数据处理(业务逻辑方面)
**而模板则将取到视图函数的数据结果进行展现(视图展现方面)**
**代码结构清晰,耦合度低**
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>
1.在项目下建立 templates 文件夹,用于存放全部的模板文件,并在目录下建立一个模板html文件temp_demo1.html
2.设置 templates 文件夹属性以便可以在代码中有智能提示
3.设置 html 中的模板语言,以便在 html 有智能提示
4.建立视图函数,将该模板内容进行渲染返回
5.代码中传入字符串,列表,字典到模板中
过滤器的本质就是函数。有时候咱们不只仅只是须要输出变量的值,咱们还须要修改变量的显示,甚至格式化、运算等等,而在模板中是不能直接调用 Python 中的某些方法,那么这就用到了过滤器。
过滤器的使用方式为:变量名 | 过滤器。
在 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>
safe:禁用转义
capitalize:把变量值的首字母转成大写,其他字母转小写
lower:把值转成小写
upper:把值转成大写
title:把值中的每一个单词的首字母都转成大写
reverse:字符串反转
过滤器的本质是函数。当模板内置的过滤器不能知足需求,能够自定义过滤器。自定义过滤器有两种实现方式:
一种是经过Flask应用对象的 add_template_filter 方法
经过装饰器来实现自定义过滤器
重要:自定义的过滤器名称若是和内置的过滤器重名,会覆盖内置的过滤器。
经过调用应用程序实例的 add_template_filter 方法实现自定义过滤器。该方法第一个参数是函数名,第二个参数是自定义的过滤器名称:
def do_listreverse(li):
# 经过原列表建立一个新列表
temp_li = list(li) //这样不会对原来的列表进行修改
# 将新列表进行返转
temp_li.reverse()
return temp_li
app.add_template_filter(do_listreverse,'lireverse')
用装饰器来实现自定义过滤器。装饰器传入的参数是自定义的过滤器名称。
@app.template_filter('lireverse')
def do_listreverse(li):
# 经过原列表建立一个新列表
temp_li = list(li)
# 将新列表进行返转
temp_li.reverse()
return temp_li
对宏(macro)的理解:
把它看做 Jinja2 中的一个函数,它会返回一个模板或者 HTML 字符串为了不反复地编写一样的模板代码,出现代码冗余,能够把他们写成函数以进行重用须要在多处重复使用的模板代码片断能够写入单独的文件,再包含在全部模板中,以免重复
对模板继承(block)的理解:
模板继承是为了重用模板中的公共内容。通常Web开发中,继承主要使用在网站的顶部菜单、底部。这些内容能够定义在父模板中,子模板直接继承,而不须要重复书写。
对包含(include)的理解:
Jinja2模板中,除了宏和继承,还支持一种代码重用的功能,叫包含(Include)。它的功能是将另外一个模板整个加载到当前模板中,并直接渲染。包含在使用时,若是包含的模板文件不存在时,程序会抛出TemplateNotFound异常,能够加上 ignore missing 关键字。若是包含的模板文件不存在,会忽略这条include语句。
后续进行后面的内容:
orm
数据库的迁移操做
重点内容均在后续,前面内容须要搞明白.....
# 以上均为刘亚杰帮忙整理,在此表示感谢。
2019-07-03 07:44:28