Flask初识
一、Flask初识
1、Flask介绍
Flask是一个使用 Python 编写的轻量级 Web 应用框架。其 WSGI 工具箱采用 Werkzeug服务 ,模板引擎则使用 Jinja2 。
Flask使用 BSD 授权。
Flask也被称为 “microframework” ,因为它使用简单的核心,用 extension 增加其他功能。Flask没有默认使用的数据库、窗体验证工具。
然而,Flask保留了扩增的弹性,可以用Flask-extension加入这些功能:ORM、窗体验证工具、文件上传、各种开放式身份验证技术
对于Werkzeug本质是Socket服务端,其用于接收http请求并对请求进行预处理,然后触发Flask框架,
开发人员基于Flask框架提供的功能对请求进行相应的处理,并返回给用户,如果要返回给用户复杂的内容时,
需要借助jinja2模板来实现对模板的处理,即:将模板和数据进行渲染,将渲染后的字符串返回给用户浏览器。
下载:
pip install flask
2、werkzeug
- werkzeug类比django的wsgiref模块,封装了sorket
- from werkzeug.wrappers import Request, Response
- from werkzeug.serving import run_simple
- @Request.application
- def hello(request):
- return Response('Hello World!')
- if __name__ == '__main__':
- run_simple('localhost', 5000, hello)
二、基本的使用
1、FlaskDemo
- from flask import Flask
- # 实例化一个Flask对象 给它指定一个名字
- app = Flask(__name__) # 名字可以任意起,但是一般我们使用__name__
- @app.route("/") # 这是路由(http://127.0.0.1:5000/),就是用户访问的url(默认端口是5000)
- def hello_world(): # 路由对应的处理函数
- return "hello World!"
- if __name__ == '__main__':
- app.run() # 启动Flask
2、登录Demo
- 1. Flask代码
- from flask import Flask, render_template, request, redirect
- # template_folder就是指定你去哪个文件夹找你要渲染的html页面
- # 默认是templates,可以修改,修改后你项目中存放html页面的文件夹名要对应更改
- app = Flask(__name__, template_folder='templates')
- @app.route("/login", methods=["GET", "POST"]) # method:指定允许请求的方式,不写-->默认只有GET
- def login():
- if request.method == 'GET':
- return render_template('login.html')
- if request.method == 'POST':
- username = request.form.get('username') # request.form:从表单获取数据
- pwd = request.form.get('pwd')
- if username == 'xiaoming' and pwd == '':
- return redirect('/index') # redirect重定向
- else:
- return render_template('login.html', error='用户名或者密码错误')
- # 参数可以返回多个,用字典的形式返回,但是要解耦 **{}
- # return render_template("login.html", **{"error": "用户名或密码错误"})
- @app.route('/index')
- def index():
- return render_template("index.html")
- if __name__ == '__main__':
- app.run()
- 2. login.html
- <body>
- <form action="" method="post">
- <input type="text" name="username">
- <input type="text" name="pwd">
- <input type="submit">
- <p>{{error}}</p>
- </form>
- </body>
- 3. index.html
- <body>
- <h1>这是首页</h1>
- <img src="/static/1.JPG" alt="">
- </body>
- 4. 注意tempaltes以及static的配置
- app = Flask(__name__, template_folder='templates', static_folder='static')
- tempaltes是存放html页面的文件夹,可以在实例化Flask的时候通过参数template_folder指定文件夹的名字。
- 默认是tempaltes。
- static是存放静态文件(图片、css、js等)的文件夹,可以在实例化Flask的时候通过参数static_folder指定文件夹的名字。
- 默认是static。
- static_url_path='/xxx' 它是一个路径,指向static的路径,也就是static路径的别名(不是真正的static路径),
- 如果设置了static_url_path,那么静态文件有两种方式可以访问到:
- "/项目路径/static/静态文件"
- "/xxx/静态文件"
- app = Flask(__name__,template_folder='templates', static_folder='static', static_url_path='/xxx')
小结
从上面的demo可以看出,flask的响应对象:
Django | Flask |
return HttpResponse("字符串") | return "字符串" |
return render("login.html") | return render_template("login.html") |
return redirect("/index") | return redirect("/index") |
return send_file("文件") | |
return jsonify(dict) |
补充
1.send_file
- from flask import Flask, send_file
- app = Flask(__name__)
- @app.route("/get_file")
- def get_file():
- return send_file('j.JPG') # 返回文件类型的(这里的j.JPG是一张图片)
- """
- send_file会把文件打开,并把文件流发送到客户端,
- send_file还会自动识别文件的类型(图片、视频、文本等),把文件类型声明在响应的content-Type,
- 最后浏览器会从content-Type里识别出文件的类型(图片、视频、文本等)并显示在页面上
- """
- if __name__ == '__main__':
- app.run()
2.jsonify
- from flask import Flask, jsonify
- app = Flask(__name__)
- @app.route("/get_json")
- def get_json():
- import json
- dic = {"name": "zbj", "age": 17}
- jdic = json.dumps(dic)
- return jdic
- @app.route("/get_jsonify")
- def get_jsonify():
- dic = {"name": "zbj", "age": 17}
- return jsonify(dic)
- """
- 上面两种方式有什么不同呢?
- 1.使用json.dumps序列化后的字典,再返回的字符串,
- 它其实就是字符串,它的Content-Type: text/html;
- 浏览器并不知道你发的是json格式的字符串,
- 那么在前端就会认为你是普通的字符串,不能使用前端字典(Object)的方法(比如用 . 取值等)。
- 2.使用jsonify返回的字典,它返回的是标准的json格式的字符串,
- 它的Content-Type: application/json;
- 浏览器现在是知道你发的是json格式的字符串,
- 那么在前端就会帮你进行处理成为前端的Object,可以使用前端Object的所有方法
- """
- if __name__ == '__main__':
- app.run()
3.request.xxx
- from flask import Flask, request
- app = Flask(__name__)
- """
- flask中的request.args == Django中的request.GET
- 都是接收url中的参数
- """
- @app.route("/get_args")
- def get_args():
- # 浏览器访问:http://127.0.0.1:5000/get_args?id=1&name=zbj
- print(request.method) # GET
- print(request.url) # http://127.0.0.1:5000/get_args?id=1&name=zbj
- print(request.cookies) # {}
- print(request.args) # ImmutableMultiDict([('id', '1'), ('name', 'zbj')])
- print(request.args.get("id")) # args本质上是一个字典,这里显示:1
- print(request.args["name"]) # zbj
- print(request.args.to_dict()) # {'id': '1', 'name': 'zbj'}
- return "hello"
- if __name__ == '__main__':
- app.run()
4.request.form
- from flask import Flask, request
- app = Flask(__name__)
- @app.route("/get_form")
- def get_form():
- # 当前端页面提交了一个表单,表单内容是:username: zzbj
- # 使用request.form接收表单中提交的数据
- print(request.form) # ImmutableMultiDict([('username', 'zzbj')])
- print(request.form.get("username")) # zzbj
- print(request.form.to_dict()) # {"username": "zzbj"}
- return render_template("login.html")
- if __name__ == '__main__':
- app.run()
3、配置文件
- 1. 配置文件信息
- flask中的配置文件是一个flask.config.Config对象(继承字典)
- 默认配置为:
- {
- 'ENV': production生产环境
- 'DEBUG': get_debug_flag(default=False), 是否开启Debug模式
- 'TESTING': False, 是否开启测试模式
- 'PROPAGATE_EXCEPTIONS': None,
- 'PRESERVE_CONTEXT_ON_EXCEPTION': None,
- 'SECRET_KEY': None, session是否加盐
- 'PERMANENT_SESSION_LIFETIME': timedelta(days=31),
- 'USE_X_SENDFILE': False,
- 'LOGGER_NAME': None, 日志名称
- 'LOGGER_HANDLER_POLICY': 'always',
- 'SERVER_NAME': None,
- 'APPLICATION_ROOT': None,
- 'SESSION_COOKIE_NAME': 'session',
- 'SESSION_COOKIE_DOMAIN': None,
- 'SESSION_COOKIE_PATH': None,
- 'SESSION_COOKIE_HTTPONLY': True,
- 'SESSION_COOKIE_SECURE': False,
- 'SESSION_REFRESH_EACH_REQUEST': True,
- 'MAX_CONTENT_LENGTH': None,
- 'SEND_FILE_MAX_AGE_DEFAULT': timedelta(hours=12),
- 'TRAP_BAD_REQUEST_ERRORS': False,
- 'TRAP_HTTP_EXCEPTIONS': False,
- 'EXPLAIN_TEMPLATE_LOADING': False,
- 'PREFERRED_URL_SCHEME': 'http',
- 'JSON_AS_ASCII': True,
- 'JSON_SORT_KEYS': True,
- 'JSONIFY_PRETTYPRINT_REGULAR': True,
- 'JSONIFY_MIMETYPE': 'application/json',
- 'TEMPLATES_AUTO_RELOAD': None,
- }
- 2. 修改flask配置信息的方式
- Flask的配置信息是存在app.config里面的,
- 我们可以在实例化Flask对象之后,打印一下app.config来查看,
- app.config是所有配置信息组成的字典。
- 方式一
- 既然是字典,就可以通过字典的方式进行修改:
- 通过app.config["xxx"] = xxx来更改配置信息,注意:config里面的键是大写的!!!
- 例如:
- app.config["DEBUG"] = True
- 注意:不建议这么使用
- 方式二
- 可以直接通过Flask的实例化对象对配置进行修改,注意:这里是小写的
- app.debug = True
- 方式三
- 通过app.config.from_object("python类或类的路径")来配置
- 1.flask项目代码
- from flask import Flask
- app = Flask(__name__)
- app.config.from_object('settings.DEVConfig')
- # app.config["DEBUG"] = True
- # app.debug = True
- @app.route('/')
- def index():
- print(app.config)
- return "主页"
- if __name__ == '__main__':
- app.run()
- 2.settings.py
- class DEVConfig(object):
- DEBUG = True
- class TestingConfig(object):
- TESTING = True
4、路由系统
- 1. 路由参数
- @app.route('/user/<username>') # 参数是字符串类型
- @app.route('/book/<int:id>') # 指定参数是int类型
- @app.route('/book/<float:id>') # 指定参数是float类型
- @app.route('/post/<path:path>')
- 注意:指定参数类型(int、float等)的时候,冒号两边不能有空格,即int:id,不能是int: id
- # 路由默认支持的参数
- DEFAULT_CONVERTERS = {
- 'default': UnicodeConverter, # 字符串
- 'string': UnicodeConverter, # 字符串
- 'any': AnyConverter, # 任意
- 'path': PathConverter, # 路径
- 'int': IntegerConverter, # 整数
- 'float': FloatConverter, # 小数
- 'uuid': UUIDConverter, # uuid
- }
- 2. 路由的命名
- 通过给route设置endpoint参数,可以给路由进行命名,如果不设置endpoint,默认的路由命名是函数名
- @app.route('/book/<int:id>', endpoint='book') # 默认是函数名字
- 3. 命名路由的反向解析
- from flask import Flask, url_for
- url_for("路由的名字", nid=32425)
- 4. 例子
- from flask import Flask, redirect, url_for
- app = Flask(__name__)
- @app.route('/book/<int:id>', endpoint='book')
- def book(id):
- print(id, type(id))
- return "BOOK"
- @app.route('/index', endpoint='index')
- def index():
- return redirect(url_for('book', id=1))
- if __name__ == '__main__':
- app.run()
5、模板
1. 跟Django比较,相同点与不同点
相同点:
Flask模板的使用的是JinJa2模板,语法基本上跟Django无差别,只是在一些细节上有些不同。
Django模板的基本使用可参考:
https://www.cnblogs.com/Zzbj/p/9892169.html
https://www.cnblogs.com/Zzbj/p/9898526.html
不同点:
flask模板中函数执行要加括号,Django的模板使用函数是不需要括号的
flask模板中字典的取值可以使用 . 点,也使用get,Django模板中字典取值只能使用 . 点
flask模板中列表的取值可以使用 . 点,也使用[索引],Django模板中列表取值只能使用 . 点
支持创建一个函数通过render_template页面的形式传递到模板中
2. 示例
- from flask import Flask, render_template
- app = Flask(__name__)
- book_list = [
- {"id": 1, "title": "三国演义"},
- {"id": 2, "title": "水浒传"},
- {"id": 3, "title": "西游记"},
- {"id": 4, "title": "红楼梦"},
- ]
- name = 'ZBJ的图书'
- my_dict = {"age": 18}
- my_list = [1, 2]
- def my_func():
- return '<h2>把自定义函数传到模板</h2>'
- @app.route('/book',)
- def book():
- return render_template('book.html', **{'book_list': book_list, 'name': name, 'my_dict': my_dict, 'my_list': my_list , 'my_func': my_func})
- if __name__ == '__main__':
- app.run()
python代码
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>Title</title>
- </head>
- <body>
- <h1>{{name}}</h1>
- <table>
- <thead>
- <tr>
- <th>title</th>
- <th>操作</th>
- </tr>
- </thead>
- <tbody>
- {% for book in book_list %}
- <tr>
- <td>{{book.title}}</td>
- <td>删除 | 编辑</td>
- </tr>
- {% endfor %}
- </tbody>
- </table>
- <hr>
- {{my_func()|safe}}
- <hr>
- 字典get取值:{{my_dict.get("age", 0)}}
- <br>
- 字典 . 点取值:{{my_dict.age}}
- <br>
- <hr>
- 列表索引取值:{{my_list[0]}}
- <br>
- 列表 . 点取值:{{my_list.1}}
- <hr>
- </body>
- </html>
模板代码
6、请求和响应
- from flask import Flask
- from flask import request
- from flask import render_template
- from flask import redirect
- from flask import make_response
- app = Flask(__name__)
- @app.route('/login', methods=['GET', "POST"])
- def login():
- """
- 1、请求相关信息
- request.method 请求的方式
- request.args 请求的url的参数 ?
- request.form form表单的数据
- request.values
- request.cookies cookies
- request.headers 请求头
- request.path 请求的url路径(不含参数 ?)
- request.full_path 请求的url路径(包含参数 ?)
- request.script_root 脚本的地址
- request.url
- request.base_url
- request.url_root
- request.host_url
- request.host
- request.files 上传过来的文件数据
- 文件的一些使用方法
- obj = request.files['the_file_name'] 获取文件名
- obj.save('/uploads/' + secure_filename(f.filename)) 把文件数据保存在服务器
- 2、响应相关信息
- 1. 这几种是把响应直接放在响应体里面,然后直接返回
- return "字符串"
- return render_template('html模板路径',**{})
- return redirect('/index.html')
- 2. 下面这种是先封装响应对象,然后对响应对象做一些操作,再把响应对象返回
- response = make_response(render_template('index.html')) response是flask.wrappers.Response类型
- response.delete_cookie('key') 删除cookie
- response.set_cookie('key', 'value') 设置cookie
- response.headers['X-Something'] = 'value' 设置响应头
- return response 最后返回
- """
- return "看注释吧!"
- if __name__ == '__main__':
- app.run()
7、session
session对象,它允许你在不同请求间存储特定用户的信息。
它是在 Cookies 的基础上实现的,并且对 Cookies 进行加密,你需要设置一个密钥。
session的加密默认用的是md5的加密方式,它规定了必须加盐,这个盐secret_key在配置信息中可以设置。
通过配置信息的secret_key设置盐:
app.secret_key = '嘿嘿嘿'
app.config['SECRET_KEY'] = '嘿嘿嘿'
设置session
session['xxx'] = 'xxx'
取session值
session.get('xxx', '')
删除session
session.pop('xxx', None)
示例
1. python代码
- # python代码
- from flask import Flask, session, redirect, url_for, request, render_template
- # 注意:导入的时候session跟sessions是不同的,注意 s
- app = Flask(__name__)
- # session必须设置session加密的盐
- app.secret_key = '嘿嘿嘿'
- @app.route('/login', methods=['GET', 'POST'])
- def login():
- if request.method == "POST":
- username = request.form.get('username')
- pwd = request.form.get('pwd')
- if username == 'xiaoming' and pwd == '':
- session['username'] = username
- return redirect(url_for('index'))
- return render_template('login.html', error="用户名或者密码错误")
- return render_template('login.html')
- @app.route('/index')
- def index():
- if session.get('username', ''):
- return "用户已登录,这是首页"
- return "用户未登录,请去登录页面"
- @app.route('/logout')
- def logout():
- session.pop('username', None)
- return redirect(url_for('index'))
- if __name__ == '__main__':
- app.run()
2. login.html
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>Title</title>
- </head>
- <body>
- <form action="" method="post">
- <input type="text" name="username">
- <input type="text" name="pwd">
- <input type="submit">
- <p>{{error}}</p>
- </form>
- </body>
- </html>
8、flash
1、flash原理
flash是一个基于session实现的用于保存数据的集合,特点是使用一次就删除.一次的意思是一次请求的开始,请求的结束,
在这段时间是可以随便使用的,但是请求结束后,第二次请求进来的时候,就取不到值了。
它的原理:
设置值的时候 session["xxx"] = value
取值 session.pop("xxx")
注意:因为flash是基于session的,因此也要设置SECRET_KEY
设置方式一:
直接设置值
flash(值1)
flash(值2)
设置值:
flash('小明')
flash('小东')
取值:
name = get_flashed_messages() # ['小明', '小东']
注意:
flash是一个列表,多次flash设置值,都是append进一个列表的,
取值的时候,就把那个列表取出来,然后把值清空,
再次取值的时候,就只能取到一个空列表。
设置方式二:
给flash分类,flash有一个参数category,就是分类的意思,
不设置的话,所有flash都是message这个分类,我们可以根据需要给flash的值设置分类。
flash(值1, 分类名1)
flash(值2, 分类名2)
设置值:
flash('小明', 'ming')
flash('小东')
取值:
name1 = get_flashed_messages() # ['小明', '小东']
name2 = get_flashed_messages(category_filter=['ming']) # ['小明']
2、Demo
1. flash原理Demo
- from flask import Flask, session
- app = Flask(__name__)
- app.secret_key = '雅蠛蝶'
- @app.route('/set')
- def set():
- session['name'] = '小明'
- return "SET"
- @app.route('/get')
- def get():
- name = session.pop('name')
- print(name)
- return "GET"
- if __name__ == '__main__':
- app.run()
- 取了一次值后,下次去get,就取不到值了
2. flashDemo
- from flask import Flask, flash, get_flashed_messages
- app = Flask(__name__)
- app.config['SECRET_KEY'] = '雅蠛蝶'
- @app.route('/set')
- def set_flash():
- flash('小明', 'ming')
- flash('小东')
- return "SET_FLASH"
- @app.route('/get')
- def get_flash():
- name1 = get_flashed_messages()
- print(name1) # ['小明', '小东']
- name2 = get_flashed_messages(category_filter=['ming'])
- print(name2) # ['小明']
- return "GET_FLASH"
- if __name__ == '__main__':
- app.run()
9、中间件
9-1、小预习
Flask的中间件跟Django的不太一样,我们需要从源码去理解,
需要用到类的一些内置方法(双下方法):__init__、__call__
1. 类
- class A():
- def __init__(self):
- print("init")
- def __call__(self, *args, **kwargs):
- print("call")
- a = A() # init
- a() # call
- 类加括号:实例化一个对象-->调用__init__方法
- 对象加括号:调用__call__方法
2. werkzeug启动flask项目的原理
- from werkzeug.wrappers import Request, Response
- from werkzeug.serving import run_simple
- @Request.application
- def hello(request):
- return Response('Hello World!')
- if __name__ == '__main__':
- run_simple('localhost', 5000, hello)
- 最后启动项目的是执行了run_simple这个方法,
- 这个方法有三个参数:
- localhost:域名
- 5000:端口
- hello:函数名
- 意思就是:在这个域名这个端口,调用hello这个函数
9-2、app.run的源码基本流程
(werkzeug是我们封装sorket的地方~源码是从run_simple方法开始走的)
app.run():
调用了werkzueg的run_simple方法 run_simple(host, port, self, **options)
这个方法中是self是我们的app, 我们知道werkzeug会执行app()
这是我们程序的入口,会执行我们app.__call__方法
那现在如果我想在请求进来之前做一些事情,以及请求结束以后做一些事情
那么就可以实现中间件的效果
app.run() -->
run_simple(host, port, self, **options) -->
self() --> app() --> app是Flask的实例化对象,因此会调用Flask这个类的__call__ -->
__call__把app.wsgi_app执行后的结果返回了,return self.wsgi_app(environ, start_response)[self就是app],
environ是请求来的最原生的数据-->
也就是说,flask所有请求进来时,都会从Flask的__call__开始执行,
我们只需在__call__这个方式的执行前后做一些事情,就可以实现中间件的功能了。
9-3、实现方式
1. 改源码(不建议使用)
- def __call__(self, environ, start_response):
- """The WSGI server calls the Flask application object as the
- WSGI application. This calls :meth:`wsgi_app` which can be
- wrapped to applying middleware."""
- # return self.wsgi_app(environ, start_response)
- print("开始之前")
- ret = self.wsgi_app(environ, start_response)
- print("请求之后")
- return ret
2. 中间件类
- from flask import Flask
- app = Flask(__name__)
- class Middleware(object):
- def __init__(self, old_call):
- self.old_call = old_call
- def __call__(self, *args, **kwargs):
- print('请求来之前做的事情')
- ret = self.old_call(*args, **kwargs)
- print('请求来到之后做的事情')
- return ret
- app.wsgi_app = Middleware(app.wsgi_app)
- if __name__ == '__main__':
- app.run()
- # run_simple(host, port, self, **options)
- # run_simple执行了 self()
- # self = app
- # app() 执行Flask的__call__
- # __call__返回app.wsgi_app()的执行结果
- # 我重写了app.wsgi_app--> Middleware(app.wsgi_app)()
- 注意:通常我们不会这样去实现中间件,因为Flask中有一些特殊的装饰器能够帮我们实现中间件的功能,哈哈哈哈~~~
10、特殊的装饰器
需求:
我们很多访问都是需要进行登录认证,那么在flask中如何实现登录认证的功能
1. 小预习
- # python的装饰器
- from functools import wraps
- # 被装饰器装饰的函数名字会变成内部的函数名
- # 我们需要给inner函数加个装饰器来修改inner函数的信息
- def wrapper(func):
- @wraps(func)
- def inner(*args, **kwargs):
- ret = func(*args, **kwargs)
- return ret
- return inner
- @wrapper
- def index(x, y):
- return x + y
- res = index(1, 2)
- print(res)
- print(index.__name__)
2. 使用python装饰器实现认证功能
- 给每个需要认证的函数都加上自定义的认证装饰器
- from flask import Flask, session, redirect, url_for
- from functools import wraps
- app = Flask(__name__)
- app.secret_key = '哈哈'
- # 实现登录 我们不可能每个视图函数都去获取session然后判断
- # 我们可以利用装饰器
- # 注意装饰器装饰完函数名的问题
- # 注意装饰器执行顺序问题
- # 实现认证的装饰器
- def auth(func):
- @wraps(func)
- def inner(*args, **kwargs):
- if not session.get('username'):
- return redirect(url_for('login'))
- ret = func(*args, **kwargs)
- return ret
- return inner
- @app.route('/login')
- def login():
- session['username'] = '小明'
- return "登录成功"
- @app.route('/logout')
- def logout():
- session.pop('username')
- return "退出登录"
- # 注意:app.route和auth装饰器的顺序不能乱,必须是先认证了成功后才放行
- @app.route('/user')
- @auth
- def user():
- return "用户页面"
- @app.route('/book')
- @auth
- def book():
- return "图书页面"
- if __name__ == '__main__':
- app.run()
3. Flask提供的特殊装饰器before_request
任何请求进来,都要先执行被before_request装饰的函数,
在这个函数进行认证,其他函数就不需要每个都加认证装饰器了。
- from flask import Flask, session, redirect, url_for, request
- app = Flask(__name__)
- app.secret_key = '哈哈'
- # 任何请求进来,都要先执行这个函数
- @app.before_request
- def auth():
- if request.path == '/login' or request.path == '/':
- # 如果是登录请求,直接放行
- return None
- if session.get('username', ''):
- # 如果携带了session的请求,证明已经登录,放行
- return None
- # 否则,都跳转到登录页面
- return redirect(url_for('login'))
- @app.route('/login')
- def login():
- session['username'] = '小明'
- return "登录成功"
- @app.route('/logout')
- def logout():
- session.pop('username')
- return "退出登录"
- # 不需要加其他装饰器了
- @app.route('/user')
- def user():
- return "用户页面"
- @app.route('/book')
- def book():
- return "图书页面"
- if __name__ == '__main__':
- app.run()
11、flask常用的特殊装饰器
- 1. 请求第一次进来的时候执行
- @app.before_first_request
- 第二次请求就不会执行了
- 2. 请求每次进来都执行
- @app.before_request
- 3. 每次响应的时候执行
- @app.after_request
- 4. 发生404的时候执行
- @app.errorhandler(404)
- 5. 全局的函数,在所有html模板中都可以使用的函数,并且不需要传
- @app.template_global()
- 6.模板的过滤器,在所有html模板中都可以使用的过滤器,并且不需要传
- @app.template_filter()
7. Demo
- from flask import Flask, Request, render_template
- app = Flask(__name__)
- app.debug = True
- @app.before_first_request
- def before_first_request1():
- print('before_first_request1')
- @app.before_first_request
- def before_first_request2():
- print('before_first_request2')
- @app.before_request
- def before_request1():
- print('before_request1')
- @app.before_request
- def before_request2():
- print('before_request2')
- @app.after_request
- def after_request1(response):
- print('after_request1', response)
- return response
- @app.after_request
- def after_request2(response):
- print('after_request2', response)
- return response
- @app.errorhandler(404)
- def page_not_found(error):
- return 'This page does not exist', 404
- @app.template_global()
- def jiafa(a1, a2):
- # 在所有模板在都可以这样使用 {{ jiafa(1, 2) }}
- return a1 + a2
- @app.template_filter()
- def db(data):
- # 在所有模板在都可以这样使用 {{ "hello"|db() }}
- # 这里data就是hello
- return data[::2]
- @app.route('/')
- def hello_world():
- return render_template('hello.html')
- if __name__ == '__main__':
- app.run()
Demo
8. 注意
这几个装饰器跟Django的中间件很像,也有执行顺序的
request请求进来,顺序执行
response响应出去,倒序执行
超级注意
!!! before_request有返回值的时候还会按顺序执行after_request
!!! Django<=1.9的版本 当process_request有返回值的时候跟flask是一样的
9. 请求流程图
12、CBV视图
1. route原理
- 之前我们的视图都是FBV,那么我们的CBV要怎么实现呢?
- 实现CBV之前我们要看一下路由的实现原理
- 我们看下@app.route("/xx")做了什么:
- 首先这是一个带参数的装饰器,那么带参数的装饰器执行顺序是什么样的
- 1,先去掉@ 执行route("/xx")得到返回值
- 2,再拿返回值加上@符号去装饰接下来的函数
- route源码
- def route(self, rule, **options):
- def decorator(f):
- endpoint = options.pop('endpoint', None)
- self.add_url_rule(rule, endpoint, f, **options)
- return f
- return decorator
- 从源码中可以看出:
- route函数返回了decorator函数,也就是说实际上是用decorator这个函数去装饰我们的视图函数的
- f就是我们的视图函数,
- decorator调用了add_url_rule方法
- endpoint默认取函数名!
- 两个函数不能用一个endpoint !
- 那么实际上路由的实现原理:
- from flask import Flask
- app = Flask(__name__)
- def index():
- return "index"
- app.add_url_rule("/", endpoint="index", view_func=index)
- if __name__ == '__main__':
- app.run()
2. CBV
- # CBV需要导入views
- from flask import Flask, views, session, redirect, url_for
- from functools import wraps
- app = Flask(__name__)
- def auth(func):
- @wraps(func)
- def inner(*args, **kwargs):
- if not session.get("userinfo"):
- return "你还没登录,滚"
- ret = func(*args, **kwargs)
- return ret
- return inner
- class MyView(views.MethodView):
- methods = ["GET", "POST"]
- # 设置decorators相当于给这个CBV的所有请求方法都加了指定的装饰器
- # decorators = [auth, ]
- def get(self):
- return "GET"
- def post(self):
- return "POST"
- # 第一个参数:路由
- # 第二个参数:视图
- # name == endpoint 命名路由
- app.add_url_rule("/index", view_func=MyView.as_view(name="index"))
- if __name__ == '__main__':
- app.run()
13、自定义路由的正则匹配
- from flask import Flask, url_for
- from werkzeug.routing import BaseConverter
- app = Flask(__name__)
- class RegexConverter(BaseConverter):
- """
- 自定义URL匹配正则表达式
- """
- def __init__(self, map, regex):
- super(RegexConverter, self).__init__(map)
- self.regex = regex
- def to_python(self, value):
- """
- 路由匹配时,匹配成功后传递给视图函数中参数的值
- value默认的类型是str,我们可以设置成我们想要的类型
- :param value:
- :return:
- """
- return int(value)
- def to_url(self, value):
- """
- 使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数
- :param value:
- :return:
- """
- val = super(RegexConverter, self).to_url(value)
- return val
- # 新加一个匹配规则regex到flask中
- # 原本只有 int string等,现在还多了一个regex
- app.url_map.converters['regex'] = RegexConverter
- @app.route('/index/<regex("\d+"):nid>')
- def index(nid):
- print(url_for('index', nid=''))
- print(nid, type(nid))
- return 'Index'
- if __name__ == '__main__':
- app.run()
14、蓝图
1. 作用
为我们提供目录的划分,就是解耦用的。
2. 目录
3. 实现步骤
- 1,新建一个项目目录 项目目录下建一个同名的python包
- 2,在这个包同级下建manager.py
- 3, 在包的__init__ 实例化Flask对象
- 4,在manager.py 导入Flask的实例化对象app app.run()
- 5, 在Python包里建立views文件夹
- 任何一个文件都都可以生成蓝图对象
- from flask import BluePrint
- # 实例化蓝图对象
- userBlue = BluePrint("userBlue", __name__)
- @userBlue.route("/user")
- def user():
- return "xxx"
- 6, 把蓝图对象注册到app中
- app.register_blueprint(bookBlue, **options)
4. 注意
每个蓝图可以指定自己的模板以及静态文件~
还可以在app中注册蓝图的时候指定路由前缀~~
还可以给蓝图加before_request~~
5. 示例代码
- from flask import Flask
- from .views.book import bookBlue
- from .views.user import userBlue
- # 创建Flask的实例化对象app
- def create_app():
- app = Flask(__name__, template_folder='templates') # 指定templates
- app.register_blueprint(userBlue) # 把蓝图对象注册到app
- app.register_blueprint(bookBlue, url_prefix='/book') # 还可以指定路由的前缀
- return app
__init__.py
- from BlueDemo import create_app
- # 导入Flask的实例化对象app
- app = create_app()
- if __name__ == '__main__':
- # 启动Flask
- app.run()
manage.py
- from flask import Blueprint, render_template
- # 实例化一个蓝图对象
- userBlue = Blueprint('userBlue', __name__)
- @userBlue.route('/user')
- def user():
- return render_template('user.html')
views/user.py
- from flask import Blueprint
- # 实例化一个蓝图对象
- bookBlue = Blueprint('bookBlue', __name__)
- # bookBlue蓝图对象,注册到app的时候,指定了url_prefix,相当于做了路由的分发
- # 这个视图的路由:/book/list
- @bookBlue.route('/list')
- def list():
- return "Book_list"
- # 这个视图的路由:/book/create
- @bookBlue.route('/create')
- def create():
- return "Book_create"
views/book.py
15、CBV配合蓝图
- # CBV需要导入views
- from flask import Flask, Blueprint, views, url_for, session
- from functools import wraps
- app = Flask(__name__) # Flask实例
- userBlue = Blueprint("userBlue", __name__) # 蓝图实例
- # 登录验证的装饰器
- def auth(func):
- @wraps(func)
- def inner(*args, **kwargs):
- if not session.get("userinfo"):
- return "你还没登录,滚"
- ret = func(*args, **kwargs)
- return ret
- return inner
- # CBV类
- class Home(views.MethodView):
- # 默认写出来的请求方法都被允许,如果还是想重定制,可使用methods属性来重新定义允许的请求
- methods = ["GET", "POST"]
- # 设置decorators相当于给这个CBV的所有请求方法都加了指定的装饰器,装饰器按索引顺序执行
- decorators = [auth, ]
- def dispatch_request(self, *args, **kwargs):
- """可以写一些自己的逻辑"""
- return super(Home, self).dispatch_request(*args, **kwargs)
- def get(self):
- return "GET"
- def post(self):
- return "POST"
- # dispatch_request方法在父类views.MethodView是已经定义了的
- # 是用于根据请求的不同进入不同的方法,例如我们对上面的CBV进行get请求,那么dispatch_request方法就会把请求分配进入get()方法
- # 而不需要我们自己手动进行 request==GET 的判断。
- # 因此可以重写dispatch_request方法可以在里面先实现一些我们的逻辑,再调用父类的dispatch,当然也可以不重写
- # 第一个参数:路由
- # 第二个参数:视图(类.as_view())
- # name == endpoint 命名路由
- userBlue.add_url_rule("/index", view_func=Home.as_view(name="index")) # 在蓝图中定义路由
- app.register_blueprint(userBlue) # 把蓝图注册到app
- # 注意:当一个蓝图被声明后,如果要使用url_for进行自定寻址需要加上蓝图名称
- # 即:url_for(blue_name.endpoint)
- # 例如,上面的"/index"路由,as_view后的第一个参数(name)就是endpoint
- url_for("userBlue.index")
- if __name__ == '__main__':
- app.run()
16、蓝图的templates和项目的templates
1、首先看一下我定义的蓝图结构(使用的是CBV模式)
说明
蓝图会根据蓝图的template_folder参数去找模板,但是需要注意的是:
- 首先都是在项目级别的模板中查找,如果项目级别的templates没有我们视图指定的html文件,那么再去找蓝图级别的templates
如果有两个蓝图或多个蓝图,它们的模板目录结构一样,如果在它们的templates下也有着同名html文件,
那么后面的蓝图加载的同名html文件会是第一个蓝图中的html。- 蓝图的templates要跟实例化蓝图的py文件在同一级,否则找html文件的时候会找不到,因为蓝图查找html文件是从实例化蓝图的地方开始,向下查找
- 即我上面的结构中,蓝图的实例化在home_url.py,那么蓝图的templates就要建立在与home_url.py同一级的地方
2、示例代码
项目的__init__.py
- from flask import Flask
- from flask_session import Session
- from flask_demo.home.home_url import homeBlue
- def create_app():
- app = Flask(__name__)
- app.config.from_object('setting.DevConfig')
- Session(app)
- app.register_blueprint(homeBlue)
- return app
__init__.py
- from flask_demo import create_app
- app = create_app()
- if __name__ == '__main__':
- app.run()
manager.py
- from flask import views, session, render_template
- class Home(views.MethodView):
- methods = ["GET"]
- def get(self):
- session['login'] = 'login'
- return render_template('home.html')
- class GetSession(views.MethodView):
- def get(self):
- resp = session.get('login')
- return resp
flask_demo/home/views/home.py
- from flask import Blueprint
- from flask_demo.home.views.home import Home, GetSession
- homeBlue = Blueprint('homeBlue', __name__, template_folder='templates')
- homeBlue.add_url_rule('/home', view_func=Home.as_view('home'))
- homeBlue.add_url_rule('/get', view_func=GetSession.as_view('get'))
flask_demo/home/home_url.py
如果项目的templates没有home.html,那么就会去蓝图的templates中查找,如果项目的templates有home.html那么就使用项目templates下的home.html
Flask初识的更多相关文章
- python 全栈开发,Day119(Flask初识,Render Redirect HttpResponse,request,模板语言 Jinja2,用户登录例子,内置Session)
一.Flask初识 首先,要看你学没学过Django 如果学过Django 的同学,请从头看到尾,如果没有学过Django的同学,并且不想学习Django的同学,轻饶过第一部分 三大主流Web框架对比 ...
- 第一篇 Flask初识
一. Python 现阶段三大主流Web框架 Django Tornado Flask 对比 1.Django 主要特点是大而全,集成了很多组件,例如: Models Admin Form 等等, 不 ...
- Flask初识之安装及HelloWord程序
Python 现阶段三大主流Web框架 Django Tornado Flask 对比 1.Django 主要特点是大而全,集成了很多组件,例如: Models Admin Form 等等, 不管你用 ...
- flask学习之路
目录 flask初识 flask模板 flask的session flask路由 flask配置和实例化传参 flask蓝图 更新中
- Flask&&人工智能AI --1
Flask初识,Response三剑客,jsonify以及send_file.Request,模板语言 Jinja2,用户登录例子,内置Sessio 一.Flask初识 首先,要看你学没学过Djang ...
- python全栈开发day110-Flask基础语法
1.Flask 初识: 短小精悍,三方支持的组件多 稳定性较差 2.三行 :启动flask服务 from flask import Flask app = Flask(__name__) app.ru ...
- flask 第七章 简陋版智能玩具 +MongoDB初识和基本操作
1.简陋版web智能玩具 FAQ.py文件 import os from aip import AipSpeech, AipNlp from uuid import uuid4 "" ...
- Flask(1)- 主流web框架、初识flask
一.Python 现阶段三大主流Web框架 Django.Tornado.Flask 对比 Django 主要特点是大而全,集成了很多组件(例如Models.Admin.Form等等), 不管你用得到 ...
- Flask&&人工智能AI -- 8 HTML5+ 初识,HBuilder,夜神模拟器,Webview
昨日内容回顾 1.增删改查: 增: db.collections.insert({a:1}) // 官方不推荐了 db.collections.insertMany([{a:1},{b:1}]) in ...
随机推荐
- Linux设备驱动之IIO子系统——IIO框架数据读取
IIO DATA ACCESS IIO数据获取 只有两种方法可以使用IIO框架访问数据; 通过sysf通道进行一次性捕获,或通过IIO字符设备进行连续模式(触发缓冲). One-shot captur ...
- Django 笔记分享
Django是一个基于MVC构造的框架.但是在Django中,控制器接受用户输入的部分由框架自行处理,所以 Django 里更关注的是模型(Model).模板(Template)和视图(Views), ...
- Object.keys 及表单清空
Object.keys 返回一个所有元素为字符串的数组,其元素来自于从给定的object上面可直接枚举的属性.这些属性的顺序与手动遍历该对象属性时的一致. // simple array var ar ...
- thymeleaf的配置
1.在springboto项目中使用thymeleaf标签,必须先添加依赖,如下. <dependency> <groupId>org.springframework.boot ...
- Dynamics 365执行操作报SQL Server已超时,更改这个超时设置的方法
本人微信公众号:微软动态CRM专家罗勇 ,回复291或者20190110可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me!我的网站是 www.luoyong.me . 当执 ...
- nginx代理天地图做缓存解决跨域问题
作为一个GISer开发者,天地图是经常在项目中以底图的形式出现,其加载地址如: 天地图矢量:http://t{0-6}.tianditu.com/DataServer?T=vec_w&x={x ...
- ASP.NET Core 入门教程 5、ASP.NET Core MVC 视图传值入门
一.前言 1.本教程主要内容 ASP.NET Core MVC 视图引擎(Razor)简介 ASP.NET Core MVC 视图(Razor)ViewData使用示例 ASP.NET Core MV ...
- Oracle database link中查询会开启事务吗?
关于oracle database link,使用database link相关的查询语句是否会开启事务呢?我们知道,在数据库中一个简单的SELECT查询语句不会产生事务(select for upd ...
- The account that is running SQL Server Setup does not have one or all of the following rights: the right to back up files and directories, the right to manage auditing and the security log and the rig
安装SQL SERVER 是规则检查提示权限问题 运行secpol.msc,没有Debug program权限,添加即可,如果已加域则要在域策略修改,或退域安装后在加域.
- Mysql数据中Packet for query is too large错误的解决方法
有时,程序在连接mysql执行操作数据库时,会出现如下类似错误信息: Packet for query is too large (4230 > 1024). You can change th ...