Flask之基础
一,flask
Flask是一个基于Python开发并且依赖jinja2模板和Werkzeug WSGI服务的一个微型框架,对于Werkzeug本质是Socket服务端,其用于接收http请求并对请求进行预处理,然后触发Flask框架,开发人员基于Flask框架提供的功能对请求进行相应的处理,并返回给用户,如果要返回给用户复杂的内容时,需要借助jinja2模板来实现对模板的处理,即:将模板和数据进行渲染,将渲染后的字符串返回给用户浏览器。
“微”(micro) 并不表示你需要把整个 Web 应用塞进单个 Python 文件(虽然确实可以 ),也不意味着 Flask 在功能上有所欠缺。微框架中的“微”意味着 Flask 旨在保持核心简单而易于扩展。Flask 不会替你做出太多决策——比如使用何种数据库。而那些 Flask 所选择的——比如使用何种模板引擎——则很容易替换。除此之外的一切都由可由你掌握。如此,Flask 可以与您珠联璧合。
默认情况下,Flask 不包含数据库抽象层、表单验证,或是其它任何已有多种库可以胜任的功能。然而,Flask 支持用扩展来给应用添加这些功能,如同是 Flask 本身实现的一样。众多的扩展提供了数据库集成、表单验证、上传处理、各种各样的开放认证技术等功能。Flask 也许是“微小”的,但它已准备好在需求繁杂的生产环境中投入使用。
二,安装
注意:建议不要直接使用pycharm中提供的flask框架,对于新手来说不太友好
pip install flask
三,与其他web框架比较
1.Django 主要特点是大而全,集成了很多组件,例如: Models Admin Form 等等, 不管你用得到用不到,反正它全都有,属于全能型框架 2.Tornado 主要特点是原生异步非阻塞,在IO密集型应用和多任务处理上占据绝对性的优势,属于专注型框架 3.Flask 主要特点小而轻,原生组件几乎为0, 三方提供的组件请参考Django 非常全面,属于短小精悍型框架 Django 通常用于大型Web应用由于内置组件足够强大所以使用Django开发可以一气呵成 Tornado 通常用于API后端应用,游戏服务后台,其内部实现的异步非阻塞真是稳得一批 Flask 通常应用于小型应用和快速构建应用,其强大的三方库,足以支撑一个大型的Web应用 Django 优点是大而全,缺点也就暴露出来了,这么多的资源一次性全部加载,肯定会造成一部分的资源浪费 Tornado 优点是异步,缺点是干净,连个Session都不支持 Flask 优点是精悍简单
1.Django 主要特点是大而全,集成了很多组件,例如: Models Admin Form 等等, 不管你用得到用不到,反正它全都有,属于全能型框架 2.Tornado 主要特点是原生异步非阻塞,在IO密集型应用和多任务处理上占据绝对性的优势,属于专注型框架 3.Flask 主要特点小而轻,原生组件几乎为0, 三方提供的组件请参考Django 非常全面,属于短小精悍型框架 Django 通常用于大型Web应用由于内置组件足够强大所以使用Django开发可以一气呵成 Tornado 通常用于API后端应用,游戏服务后台,其内部实现的异步非阻塞真是稳得一批 Flask 通常应用于小型应用和快速构建应用,其强大的三方库,足以支撑一个大型的Web应用 Django 优点是大而全,缺点也就暴露出来了,这么多的资源一次性全部加载,肯定会造成一部分的资源浪费 Tornado 优点是异步,缺点是干净,连个Session都不支持 Flask 优点是精悍简单
四,flask基本使用
from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello World!' if __name__ == '__main__': app.run()
五,flask三剑客
1,response(类比django的HttpResponse)
from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello World!' if __name__ == '__main__': app.run()
2,render_template
模板默认在名叫templates文件夹下
from flask import Flask,render_template @app.route("/detail_list") def detail_list(): return render_template("detail_list.html", students=STUDENT_LIST) # 可以传值到模板中
3,redirect
同django一样,都是在响应头中加 location
from flask import Flask, redirect @app.route("/login", methods=("GET", "POST")) def login(): msg = "" if request.method == "POST": username = request.form.get("username") pwd = request.form.get("pwd") if username == "qqq" and pwd == "123": session["user"] = username return redirect("/index") msg = "用户名或者密码错误" return render_template("login.html", msg=msg)
六,flask中的request
每个框架中都有处理请求的机制(request),但是每个框架的处理方式和机制是不同的;
flask中基于上下文管理,使用的是一个全局的request对象,每次请求都是本次请求的request;
flaskrequest对象中封装的属性有:
1.request.method
Flask 的 request 中给我们提供了一个 method 属性里面保存的就是前端的请求的方式
print(request.method) # POST 看来可以使用这种方式来验证请求方式了
2.request.form
Form表单中传递过来的值 使用 request.form 中拿到
print(request.form) # ImmutableMultiDict([('user', 'sss'), ('pwd', '123')]) # ImmutableMultiDict 它看起来像是的Dict 就用Dict的方法取值试一下吧 print(request.form["user"]) # sss print(request.form.get("pwd")) # 123 # 看来全部才对了, ImmutableMultiDict 似乎就是个字典,再来玩一玩它 print(list(request.form.keys())) # ['user', 'pwd'] 看来是又才对了 #如果以上所有的方法你都觉得用的不爽的话 req_dict = dict(request.form) print(req_dict) # 如果你觉得用字典更爽的话,也可以转成字典操作(这里有坑)
print(request.form.to_dict()) # ImmutableMultiDict([('username', 'qqq'), ('pwd', '123')]) 转化为python的字典格式
3,request.args
request.args 中保存的是url中传递的参数,用法和request.form操作一样
4,request.values
获取所有的数据
5,request.data
content_type不是标准类型的数据,都会丢到data中
request 是基于 mimetype 进行处理的
mimetype的类型 以及 字符串儿 : http://www.w3school.com.cn/media/media_mimeref.asp
如果不属于上述类型的描述,request就会将无法处理的参数转为Json存入到 data 中
其实我们可以将 request.data , json.loads 同样可以拿到里面的参数
如果url和form中的Key重名的话,form中的同名的key中value会被url中的value覆盖 # http://127.0.0.1:5000/req?id=1&user=20 print(request.values.to_dict()) # {'user': 20 'pwd': 'DragonFire', 'id': '1'}
6 .request.files
如果遇到文件上传的话,request.files 里面存的是你上传的文件,但是 Flask 在这个文件的操作中加了一定的封装,让操作变得极为简单
前端代码注意: enctype
后端这样写
print(request.files) # ImmutableMultiDict([('file', <FileStorage: 'DragonFire.txt' ('text/plain')>)]) print(request.files["file"]) # <FileStorage: 'DragonFire.txt' ('text/plain')> my_file = request.files["file"] my_file.save("sss.txt") # 保存文件,里面可以写完整路径+文件名
这样我们就成功的保存了一个名叫 "sss.txt" 的文件了,操作还是很简单的
7,其他
print(request.path)# /req # 当前url路径的上一级路径 print(request.script_root) # # 当前url的全部路径 print(request.url) # http://127.0.0.1:5000/req # 当前url的路径的上一级全部路径 print(request.url_root ) # http://127.0.0.1:5000/
# print(request.url) # 请求的url # print(request.path) # 请求的路径 print(request.headers) # 请求头 print(request.cookies) # cookies
七,flask中的session
flask中的自带的session,特点很明显:它将session存在浏览器的cookies中,以session为键,对真实session经过加密的随机字符串为值存储的;
基本使用:
from flask import Flask,session app = Flask(__name__) app.secret_key = "类似于加盐,用于加密那个存储在cookie中的 session那个键值对" @app.route("/login", methods=("GET", "POST", )) def login(): msg = "" if request.method == "POST": username = request.form.get("username") pwd = request.form.get("pwd") if username == "qqq" and pwd == "123": session["user"] = username # 设置session return redirect("/home/") msg = "用户名或者密码错误!" return render_template("login.html", msg=msg)
这样浏览器cookies 中的 就会存储了键为session,值位加密字符串的键值对了;
cookies 中 session 存储的是通过 secret_key 加密后的 key , 通过这个 key 从flask程序的内存中找到用户对应的session信息
怎么用 session 进行验证呢?
@app.route("/student_list") def student(): if session.get("user"): return render_template("student_list.html", student=STUDENT_DICT) return redirect("/login")
八,flask中的路由
1,flask中视图路由的添加
- 使用给视图函数加装饰器的方式:@app.route()
- 使用flask实例对象调用app.add_url_rule()的方式
2. 参数配置
methods : 当前 url 地址,允许访问的请求方式
@app.route("/info", methods=["GET", "POST"]) def student_info(): stu_id = int(request.args["id"]) return f"Hello Old boy {stu_id}" # Python3.6的新特性 f"{变量名}"
endpoint : 反向url地址,默认为视图函数名 (url_for)
from flask import url_for @app.route("/info", methods=["GET", "POST"], endpoint="r_info") def student_info(): print(url_for("r_info")) # /info stu_id = int(request.args["id"]) return f"Hello Old boy {stu_id}" # Python3.6的新特性 f"{变量名}"
defaults : 视图函数的参数默认值{"nid":1}
from flask import url_for @app.route("/info", methods=["GET", "POST"], endpoint="r_info", defaults={"nid": 100}) def student_info(nid): print(url_for("r_info")) # /info # stu_id = int(request.args["id"]) print(nid) # 100 return f"Hello Old boy {nid}" # Python3.6的新特性 f"{变量名}"
strict_slashes : url地址结尾符"/"的控制 False : 无论结尾 "/" 是否存在均可以访问 , True : 结尾必须不能是 "/"
# 访问地址 : /info @app.route("/info", strict_slashes=True) def student_info(): return "Hello Old boy info" # 访问地址 : /infos or /infos/ @app.route("/infos", strict_slashes=False) def student_infos(): return "Hello Old boy infos"
redirect_to : url地址重定向
# 访问地址 : /info 浏览器跳转至 /infos @app.route("/info", strict_slashes=True, redirect_to="/infos") def student_info(): return "Hello" @app.route("/infos", strict_slashes=False) def student_infos(): return "Hello"
subdomain : 子域名前缀 subdomian="test" 这样写可以得到 test.myserver_name 前提是app.config["SERVER_NAME"] = "myserver_name"
app.config["SERVER_NAME"] = "myserver_name" @app.route("/info",subdomain="test") def student_info(): return "Hello" # 访问地址为: test.myserver_name /info
3 .动态参数路由
from flask import url_for # 访问地址 : http://127.0.0.1:5000/info/1 @app.route("/info/<int:nid>", methods=["GET", "POST"], endpoint="r_info") # 不指定类型,默认为string def student_info(nid): print(url_for("r_info",nid=2)) # /info/2 return f"Hello Old boy {nid}" # Python3.6的新特性 f"{变量名}"
九,flask的配置
1,对已经实例化Flask对象(即app)配置
Flask的配置就是在 app.config 中添加一个键值对,但是你存进去的键必须是config中应该存在的,如果不再存在的话,它会默认无用,就这么放着;
默认的config中的键有:
{ 'DEBUG': False, # 是否开启Debug模式 'TESTING': False, # 是否开启测试模式 'PROPAGATE_EXCEPTIONS': None, # 异常传播(是否在控制台打印LOG) 当Debug或者testing开启后,自动为True 'PRESERVE_CONTEXT_ON_EXCEPTION': None, # 一两句话说不清楚,一般不用它 'SECRET_KEY': None, # 之前遇到过,在启用Session的时候,一定要有它 'PERMANENT_SESSION_LIFETIME': 31, # days , Session的生命周期(天)默认31天 'USE_X_SENDFILE': False, # 是否弃用 x_sendfile 'LOGGER_NAME': None, # 日志记录器的名称 'LOGGER_HANDLER_POLICY': 'always', 'SERVER_NAME': None, # 服务访问域名 'APPLICATION_ROOT': None, # 项目的完整路径 'SESSION_COOKIE_NAME': 'session', # 在cookies中存放session加密字符串的名字 'SESSION_COOKIE_DOMAIN': None, # 在哪个域名下会产生session记录在cookies中 'SESSION_COOKIE_PATH': None, # cookies的路径 'SESSION_COOKIE_HTTPONLY': True, # 控制 cookie 是否应被设置 httponly 的标志, 'SESSION_COOKIE_SECURE': False, # 控制 cookie 是否应被设置安全标志 'SESSION_REFRESH_EACH_REQUEST': True, # 这个标志控制永久会话如何刷新 'MAX_CONTENT_LENGTH': None, # 如果设置为字节数, Flask 会拒绝内容长度大于此值的请求进入,并返回一个 413 状态码 'SEND_FILE_MAX_AGE_DEFAULT': 12, # hours 默认缓存控制的最大期限 'TRAP_BAD_REQUEST_ERRORS': False, # 如果这个值被设置为 True ,Flask不会执行 HTTP 异常的错误处理,而是像对待其它异常一样, # 通过异常栈让它冒泡地抛出。这对于需要找出 HTTP 异常源头的可怕调试情形是有用的。 'TRAP_HTTP_EXCEPTIONS': False, # Werkzeug 处理请求中的特定数据的内部数据结构会抛出同样也是“错误的请求”异常的特殊的 key errors 。 # 同样地,为了保持一致,许多操作可以显式地抛出 BadRequest 异常。 # 因为在调试中,你希望准确地找出异常的原因,这个设置用于在这些情形下调试。 # 如果这个值被设置为 True ,你只会得到常规的回溯。 'EXPLAIN_TEMPLATE_LOADING': False, 'PREFERRED_URL_SCHEME': 'http', # 生成URL的时候如果没有可用的 URL 模式话将使用这个值 'JSON_AS_ASCII': True, # 默认情况下 Flask 使用 ascii 编码来序列化对象。如果这个值被设置为 False , # Flask不会将其编码为 ASCII,并且按原样输出,返回它的 unicode 字符串。 # 比如 jsonfiy 会自动地采用 utf-8 来编码它然后才进行传输。 'JSON_SORT_KEYS': True, #默认情况下 Flask 按照 JSON 对象的键的顺序来序来序列化它。 # 这样做是为了确保键的顺序不会受到字典的哈希种子的影响,从而返回的值每次都是一致的,不会造成无用的额外 HTTP 缓存。 # 你可以通过修改这个配置的值来覆盖默认的操作。但这是不被推荐的做法因为这个默认的行为可能会给你在性能的代价上带来改善。 'JSONIFY_PRETTYPRINT_REGULAR': True, 'JSONIFY_MIMETYPE': 'application/json', 'TEMPLATES_AUTO_RELOAD': None, }
以上这些Key,都可以被改写,当然他们也都是有默认值存在的,如果没有特殊情况,不要改写它的默认值
修改配置的方式大约是两种
1.直接对app.config进行修改
app.config["DEBUG"] = True
2.使用类的方式导入
首先要有一个settings.py的文件
class FlaskSetting: DEBUG = True SECRET_KEY = "DragonFire"
然后我们在Flask的启动文件中就可以这么写
from flask import Flask app = Flask(__name__) # type:Flask app.config.from_object("settings.FlaskSetting") # 这个方法是flask中的配置类Config的一个方法,同样可以把我们配置的键值对,添加到app.congig中
这叫做类导入配置
也可以直接导入一个py文件,app.config.from_pyfile("settings.py"),在文件中直接写如DEBUG=True;
2,在Flask实例化的时候,传递参数进行配置
static_folder = 'static', # 静态文件目录的路径 默认当前项目中的static目录 static_host = None, # 远程静态文件所用的Host地址,默认为空 static_url_path = None, # 静态文件目录的url路径 默认不写是与static_folder同名,远程静态文件时复用 # host_matching是否开启host主机位匹配,是要与static_host一起使用,如果配置了static_host, 则必须赋值为True # 这里要说明一下,@app.route("/",host="localhost:5000") 就必须要这样写 # host="localhost:5000" 如果主机头不是 localhost:5000 则无法通过当前的路由 host_matching = False, # 如果不是特别需要的话,慎用,否则所有的route 都需要host=""的参数 subdomain_matching = False, # 理论上来说是用来限制SERVER_NAME子域名的,但是目前还没有感觉出来区别在哪里 template_folder = 'templates' # template模板目录, 默认当前项目中的 templates 目录 instance_path = None, # 指向另一个Flask实例的路径 instance_relative_config = False # 是否加载另一个实例的配置 root_path = None # 主模块所在的目录的绝对路径,默认项目目录
我们常用的参数有
static_folder = 'static', # 静态文件目录的路径 默认当前项目中的static目录 static_url_path = None, # 静态文件目录的url路径 默认不写是与static_folder同名,远程静态文件时复用 template_folder = 'templates' # template模板目录, 默认当前项目中的 templates 目录
十,蓝图
1,蓝图基本使用
from flask import Blueprint, render_template add_stu = Blueprint("add_stu", __name__, static_folder="blue_static", static_url_path="/static_b", template_folder="blue_templates") @add_stu.route("/add") def add(): return render_template("blue_add.html")
一定要把蓝图注册到Flask实例对象中,
app = Flask(__name__, static_url_path="/sss") app.register_blueprint(add_stu) # 把蓝图注册到Flask实例化对象中
2,蓝图的作用
蓝图插拔机制,使得我们的应用更加灵活,比如某个应用的某些功能我们需要的时候可以使用蓝图的方式注册进去,不需要的时候,直接注释掉注册的那行代码,就可以去掉这个功能了!
3,蓝图的配置和app的配置一模一样
可以有自己的目录结构:
十一, 闪现
没什么暖用的闪现
from flask import Flask, flash, get_flashed_messages @app.route("/home2", endpoint="home2") @login_required # 注意装饰器的顺序 def home2(): flash("访问了home2页面") # 设置flash, res = get_flashed_messages() # 获flash中的值,是一个列表,取一次,数据就消失 print(res) return Markup("<h1>home2页面</h1>")
十二,中间件
也不能叫做中间件,flask中使用2个装饰器,实现类似django中间件的功能
from flask import Flask @app.before_request def get_session1(): print("get_session1") if request.path == "/login": # 登陆才能访问 return None if not session.get("user"): return redirect("/login") @app.before_request def get_session2(): print("get_session2") @app.after_request def process_response1(res): print("process_response2") return res @app.after_request def process_response2(res): print("process_response1") return res
执行顺序:和代码的上下有关,正常 before1, before2, after2, after1
十三,sendfile jsonify
1, sendfile
from flask import Flask, request,send_file, jsonify, @app.route("/sf") def sf(): return send_file("./app.py") # 打开文件并返回 # return send_file("1.jpg") # 可以直接返回图片 和 音频
2, jsonify
from flask import Flask, jsonify @app.route("/jt") def json_test(): ret_dict = {"name": "ggg"} return jsonify(ret_dict) # 直接返回json数据,添加content_Type: application/json