Flask学习记录
flask run启动
新版flask在linux中的运行方式
先在环境变量中指定要运行哪个文件,比如要运行的是helloworld_server.py文件
export FLASK_APP=helloworld_server
对于win环境,则使用
$env:FLASK_APP = "helloworld_server"
然后如果要运行在开发模式则需要输入:(前面是linux环境命令,后面是win环境命令)
export FLASK_ENV=development$env:FLASK_ENV="development"
然后执行(后面的可以指定ip和端口)
flask run
flask run -h 0.0.0.0 -p 50001
export FLASK_ENV=production运行在生产模式,未指明则默认为此方式 export FLASK_ENV=development运行在开发模式
flask routes查询路由
注意,需要先在环境变量中指定要运行的是哪个文件,才能使用flask routes查询这个文件中的路由
定义路由的请求方式
有三种:
GET
OPTIONS(自带)
HEAD(自带)
methods参数指定请求方式
method指定的是一个列表,里面可以有多种支持的请求方式
@app.route("/itcast1", methods=["POST"])def view_func_1(): return "hello world 1"
@app.route("/itcast1", methods=["GET", "POST"])def view_func_2(): return "hello world 2"
蓝图的使用
单一文件定义蓝图
# 8创建蓝图对象user_bp = Blueprint('user', __name__)# 9编写蓝图视图@user_bp.route('/profile')def get_profile(): return 'get_profile'# 10注册蓝图# app.register_blueprint(user_bp)# 11指定蓝图的url前缀app.register_blueprint(user_bp, url_prefix='/user')
目录定义蓝图
1、先有目录,然后有__init__.py
文件和views.py
文件
views.py
里是蓝图对应的视图代码
在__init__.py
文件中编写代码:
# 0导包from flask import Blueprint
# 1创建蓝图对象goods_bp = Blueprint('goods', __name__, url_prefix='/goods')
# 2导入views.py,不导入的话不会执行views.py中的代码,就不能访问视图函数from . import views
在views.py
文件编写代码:
# 0导入蓝图对象from . import goods_bp
# 1编写蓝图视图@goods_bp.route('/getgoods')def get_goods(): return 'goods'
2、在创建flask应用的文件里把蓝图注册到app
# 12 注册goods包中的蓝图,注意这里导包是从goods路径导入__init__.py文件中的gods_bp,如果写成from goods.__init__会访问不了蓝图from goods import goods_bpapp.register_blueprint(goods_bp)
蓝图必须显示指明静态文件目录
不像默认的static文件夹一样,蓝图里的静态文件不会被默认寻找,只有显示指定静态文件目录才会去找
admin = Blueprint("admin",__name__,static_folder'static_admin')app.register_blueprint(admin,url_prefix='/admin')
也可通过 static_url_path 改变访问路径
admin = Blueprint("admin",__name__,static_folder='static_admin',static_url_path='/lib')app.register_blueprint(admin,url_prefix='/admin')
从url路径获取参数
直接获取
# 13从url路径中获取参数@user_bp.route('/profile/<user_id>')def get_profile_by_id(user_id): return 'get_profile_by_id:{}'.format(user_id)
转换器配合正则表达式使用
# 14使用转换器配合正则表达式@user_bp.route('/profile/<int:user_id>')def get_profile_by_id(user_id): return 'get_profile_by_id:{}'.format(user_id)
转换器内置的类型:
- default
- string
- any
- path
- int
- float
- uuid
自定义转换器类型
# 15自定义转换器# 16导包并编写类from werkzeug.routing import BaseConverterclass MobileConverter(BaseConverter): """自定义转换器""" regex = r'1[3-9]\d{9}'# 17将自定义的转换器告知flask应用app.url_map.converters['mobile'] = MobileConverter# 18使用自定义转换器@app.route('/sms_codes/<mobile:mob_num>')def send_sms_code(mob_num): return 'send_sms_code:{}'.format(mob_num)
request对象的使用
request对象中封装了以下内容:
data | 记录请求的数据,并转为字符串(请求体) | * |
---|---|---|
form | 记录请求中的表单数据 | MultiDict |
args | 请求中的参数 | MultiDict |
cookies | 请求中的cookie信息 | Dict |
headers | 请求头 | EnvironHeaders |
method | 请求方法 | GET/POST |
url | 请求的URL地址 | string |
files | 请求上传的文件 | * |
从请求中获取参数
# 19request对象的使用,要先导入request包# /articles?channel_id=123@app.route('/articles')def get_articles(): channel_id = request.args.get('channel_id') return 'get_articles:{}'.format(channel_id)
从请求中获取文件
# 20提取request中的文件@app.route('/upload', methods=['POST'])def upload_file(): file = request.files['pic'] # 手动打开文件 with open('./demo.jpg', 'wb') as new_file: new_file.write(file.read()) # flask提供的保存文件方法 file.save('./demo.jpg') return 'ok'
处理响应render_template
使用模板文件夹中的文件去处理响应
# 0导包from flask import Flask, request, Blueprint
app = Flask(__name__)
# 1导入模板响应需要的包from flask import render_template# 2使用render_template函数去渲染模板,传参直接传递参数# 在模板中通过{{my_str}}和{{my_int}}去获取参数@app.route('/')def index(): # 普通变量直接传 #my_str = 'hello world' #my_int = 100 #return render_template('index.html', my_str=my_str, my_int=my_int) # 字典要保证字典中的key和模板中的变量名一致 data = dict( my_str='hello world', my_int=1233 ) return render_template('index.html', **data) # 这行传字典,等价于下面这行,所以要保证字典中的key和模板中index.html的变量名一致 # return render_template('index.html', my_str='hello world', my_int=1233)
重定向
# 3重定向,先导包from flask import redirect@app.route('/demo')def demo(): return redirect('http://www.itheima.com')
返回json
# 4返回json数据# 先导包from flask import jsonify@app.route('/json')def json(): json_dict = { "user_id": 1001, "user_name": "itcast", "age": 18 } return jsonify(json_dict)
响应的返回
# 5响应有两种返回方式# 元祖返回响应@app.route('/demo4')def demo4(): # 注意格式是(response,status,headers),如果不足3个,则从右边开始缺省 # 例如写了status,那必须要有response return '状态码为 666', 666, {'Itcast': 'python'}# make_response返回响应from flask import make_response@app.route('/demo5')def demo5(): response = make_response('状态码为 666') response.headers['Itcast'] = 'python' response.status_code = 666 return response
设置cookie只能通过make_response
# 6设置cookiefrom flask import make_response@app.route('/cookie')def set_cookie(): response = make_response('设置cookie成功') # 第一个参数是cookie的名称,第二个参数是cookie的值,第三个参数是cookie的过期时间,可以没有第三个参数 response.set_cookie('username', 'itcast', max_age=3600) return response
读取cookie的值
# 7读取cookie的值@app.route('/get_cookie')def get_cookie(): username = request.cookies.get('username') return username
删除cookie的值
# 8删除cookie@app.route('/delete_cookie')def delete_cookie(): response = make_response('删除cookie成功') response.delete_cookie('username') return response
session对象
设置session
# 9设置session,注意在使用session之前,必须先设置secret_key# 先导包from flask import session@app.route('/set_session')def set_session(): session['username'] = 'itcast' return '设置session成功'
读取session
#10读取session@app.route('/get_session')def get_session(): username = session.get('username') return '获取到session数据:{}'.format(username)
异常处理
http异常主动抛出用abort()
# 11使用abort函数,直接返回错误响应,要先导包from flask import abort@app.route('/article')def get_article(): channel_id = request.args.get('channel_id') if channel_id is None: abort(404) else: return '获取文章成功,channel_id:{}'.format(channel_id)
装饰器捕获异常
# 12捕获异常,用装饰器@app.errorhandler(500)def internal_server_error(e): return '服务器搬家了', 500# 13也可以捕获特定的异常@app.errorhandler(ZeroDivisionError)def zero_division_error(e): return '除数不能为0'@app.route('/zero')def zero(): a = 1/0 return 'zero 函数'
请求钩子hook
就是中间件的作用
请求的处理过程:pre_process -> view -> after_prosess
middleware1.pre_process() -> m2.pre_process() -> m3.pre_process()->view0 -> m3.after_process() -> m2.after_process() ->m1.after_process() -> client
中间件处理不区分具体是哪个视图,对所有视图通通生效
有四中处理的方法:
before_first_request:只在第一次接收到请求时处理,在视图前
before_request:每次请求前执行,在视图前
after_request:如果没有抛出异常,在每次请求后执行
teardown_request:在每次请求后执行
上下文
分为:请求上下文和应用上下文
请求上下文
有两个,request和session
应用上下文
current_app:当前应用
使用场景:在另一个文件中要使用到其他文件中创建的app对象,直接import有循环引用的风险
from flask import Blueprint, current_appbp = Blueprint('passprot', __name__)
@bp.route('/bp')def viewfunction(): # 这行相当于 app.config.get('Itcast') current_app.config.get('Itcast') pass
g对象:省去传递参数的声明,只在一次请求的期间传递,下一次请求存下一次请求的数据
# 18g对象可以存取当前请求的数据,免于传参# 先导包from flask import g# 普通传参方式def db_query_1(user_id, user_name): return 'db_query_1:{}, {}'.format(user_id, user_name)# g对象方式def db_query_2(): user_id = g.user_id user_name = g.user_name return 'db_query_2:{}, {}'.format(user_id, user_name)@app.route('/g')def get_user_profile(): # user_id = 123 # user_name = "itcast" # ret = db_query_1(user_id, user_name) g.user_id = 456 g.user_name = "itcast" ret = db_query_2() # 不用考虑传参 return ret
正式开始写项目
通过endpoint给路由起别名
# 起别名是给这个HelloWorldResource, '/'这一组起别名api.add_resource(HelloWorldResource, '/', endpoint='HelloWorld')
flask_result的路由
# -*- conding:utf-8 -*-# 0导包from flask import Flaskfrom flask_restful import Resource, Api
# 1创建appapp = Flask(__name__)api = Api(app)
# 2创建类视图class HelloWorldResource(Resource): def get(self): return {'hello': 'world'}
def post(self): return {'msg': 'post hello world'}
# 3注册路由,参数是类视图对应的类,和访问路径api.add_resource(HelloWorldResource, '/')
flask_result的蓝图
# 4给蓝图导包from flask import Blueprint# 5创建蓝图对象user_bp = Blueprint('user', __name__)# 6使用蓝图,api对象不再和全局的app对象绑定,而是和蓝图绑定,得到蓝图里的api对象user_api = Api(user_bp)# 7蓝图的类视图class UserProfileResource(Resource): def get(self): return {'msg': 'get user profile'}# 8用蓝图里的api对象收集路径信息,api绑定的哪个对象,收集到的路径信息就传给哪个对象user_api.add_resource(UserProfileResource, '/user/profile')# 9蓝图对象注册到全局appapp.register_blueprint(user_bp)
给类视图加装饰器
# 10给不同的请求方法加不同的装饰器,写在后面的装饰器会先被调用# 首先创建装饰器def decorator1(func): def wrapper(*args, **kwargs): print('call decorator1') return func(*args, **kwargs) return wrapper
def decorator2(func): def wrapper(*args, **kwargs): print('call decorator2') return func(*args, **kwargs) return wrapper# 11创建装饰器类class DemoResource(Resource): method_decorators = { 'get': [decorator1, decorator2], 'post': [decorator1] } # 使用了[decorator1, decorator2]两个装饰器 def get(self): return {'msg': 'get view'} # 使用了[decorator1]装饰器 def post(self): return {'msg': 'post view'} # 未使用装饰器 def put(self): return {'msg': 'put view'}# 12把类视图注册到路由api.add_resource(DemoResource, '/DemoResource')
关于请求处理参数
# 13使用RequestParser进行校验# 导包from flask_restful.reqparse import RequestParser# 创建视图类class RequestParserDemo(Resource): def get(self): # 创建RequestParser类对象 request_parser = RequestParser() # 声明参数 request_parser.add_argument('a') request_parser.add_argument('b') # 执行校验 request = request_parser.parse_args() # 可以把request当做字典,也可以当做对象 # a = request['a'] a = request.a return {'a': a}# 14把类视图注册到路由api.add_resource(RequestParserDemo, '/RequestParserDemo')
有四个字段
例如:
request_parser.add_argument('b', type=int, required=True, help='missing b', action='append')
1 required
请求是否一定要携带参数,默认为false
2 help
参数检验错误时返回到错误描述信息
3 action
对于请求参数中出现多个同名参数时的处理方式
action=‘store’
保留出现的第一个,默认
action=‘append’
以列表追加保存所有同名参数的值
4 type
描述参数应该匹配的类型
有三种可以匹配的类型
1、Python标准类型
2、Flask-RESTful提供的类型(检验类型方法在flask_restful.inputs模块中)
-
url
-
regex(指定正则表达式)
-
from flask_restful import inputsrequest_parser.add_argument('a', type=inputs.regex(r'^\d{2}&'))
-
-
natural:自然数0、1、2、3..
-
positive:正整数1、2、3..
-
int_range(low, high):整数范围
-
from flask_restful import inputsrequest_parser.add_argument('a', type=input.int_range(1, 10))
-
-
boolean
3、自定义
5 location
描述参数应该在请求数据中出现的位置
也可指明多个位置
request_parser.add_argument('text', location=['headers', 'json'])
关于响应
# 15处理响应# 导包from flask_restful import fields, marshal_with, marshal# 编写用户类class User(object): def __inti__(self, user_id, name, age): self.user_id = user_id self.name = name self.age = age# 声明需要序列化处理的字段resource_fields = { 'user_id': fields.Integer, 'name': fields.String}# 编写视图类class Demo1marshal(Resource): @marshal_with(resource_fields, envelope='data1') def get(self): user = User(1, 'itcast', 12) return userclass Demo2marshal(Resource): def get(self): user = User(1, 'itcast', 12) return marshal(user, resource_fields, envelope='data2')# 把视图类注册到路由的apiapi.add_resource(Demo1marshal, '/demo1marshal')api.add_resource(Demo2marshal, '/demo2marshal')
自定义返回的json格式
Flask-RESTful的Api对象提供了一个representation的装饰器,允许定制返回数据的呈现格式
外键可以不设,也可以设置set null,防止删除时报错