一、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

  1. werkzeug类比djangowsgiref模块,封装了sorket
  2.  
  3. from werkzeug.wrappers import Request, Response
  4. from werkzeug.serving import run_simple
  5.  
  6. @Request.application
  7. def hello(request):
  8. return Response('Hello World!')
  9.  
  10. if __name__ == '__main__':
  11. run_simple('localhost', 5000, hello)

二、基本的使用

1、FlaskDemo

  1. from flask import Flask
  2.  
  3. # 实例化一个Flask对象 给它指定一个名字
  4. app = Flask(__name__) # 名字可以任意起,但是一般我们使用__name__
  5.  
  6. @app.route("/") # 这是路由(http://127.0.0.1:5000/),就是用户访问的url(默认端口是5000)
  7. def hello_world(): # 路由对应的处理函数
  8. return "hello World!"
  9.  
  10. if __name__ == '__main__':
  11. app.run() # 启动Flask

2、登录Demo

  1. 1. Flask代码
  2. from flask import Flask, render_template, request, redirect
  3.  
  4. # template_folder就是指定你去哪个文件夹找你要渲染的html页面
  5. # 默认是templates,可以修改,修改后你项目中存放html页面的文件夹名要对应更改
  6. app = Flask(__name__, template_folder='templates')
  7.  
  8. @app.route("/login", methods=["GET", "POST"]) # method:指定允许请求的方式,不写-->默认只有GET
  9. def login():
  10. if request.method == 'GET':
  11. return render_template('login.html')
  12. if request.method == 'POST':
  13. username = request.form.get('username') # request.form:从表单获取数据
  14. pwd = request.form.get('pwd')
  15. if username == 'xiaoming' and pwd == '':
  16. return redirect('/index') # redirect重定向
  17. else:
  18. return render_template('login.html', error='用户名或者密码错误')
  19. # 参数可以返回多个,用字典的形式返回,但是要解耦 **{}
  20. # return render_template("login.html", **{"error": "用户名或密码错误"})
  21.  
  22. @app.route('/index')
  23. def index():
  24. return render_template("index.html")
  25.  
  26. if __name__ == '__main__':
  27. app.run()
  28.  
  29. 2. login.html
  30. <body>
  31. <form action="" method="post">
  32.  
  33. <input type="text" name="username">
  34. <input type="text" name="pwd">
  35. <input type="submit">
  36. <p>{{error}}</p>
  37. </form>
  38. </body>
  39.  
  40. 3. index.html
  41. <body>
  42. <h1>这是首页</h1>
  43.  
  44. <img src="/static/1.JPG" alt="">
  45.  
  46. </body>
  47.  
  48. 4. 注意tempaltes以及static的配置
  49. app = Flask(__name__, template_folder='templates', static_folder='static')
  50. tempaltes是存放html页面的文件夹,可以在实例化Flask的时候通过参数template_folder指定文件夹的名字。
  51. 默认是tempaltes
  52.  
  53. static是存放静态文件(图片、cssjs等)的文件夹,可以在实例化Flask的时候通过参数static_folder指定文件夹的名字。
  54. 默认是static
  55.  
  56. static_url_path='/xxx' 它是一个路径,指向static的路径,也就是static路径的别名(不是真正的static路径),
  57. 如果设置了static_url_path,那么静态文件有两种方式可以访问到:
  58. "/项目路径/static/静态文件"
  59. "/xxx/静态文件"
  60.  
  61. 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

  1. from flask import Flask, send_file
  2.  
  3. app = Flask(__name__)
  4.  
  5. @app.route("/get_file")
  6. def get_file():
  7. return send_file('j.JPG') # 返回文件类型的(这里的j.JPG是一张图片)
  8.  
  9. """
  10. send_file会把文件打开,并把文件流发送到客户端,
  11. send_file还会自动识别文件的类型(图片、视频、文本等),把文件类型声明在响应的content-Type,
  12. 最后浏览器会从content-Type里识别出文件的类型(图片、视频、文本等)并显示在页面上
  13. """
  14.  
  15. if __name__ == '__main__':
  16. app.run()

2.jsonify

  1. from flask import Flask, jsonify
  2.  
  3. app = Flask(__name__)
  4.  
  5. @app.route("/get_json")
  6. def get_json():
  7. import json
  8. dic = {"name": "zbj", "age": 17}
  9. jdic = json.dumps(dic)
  10. return jdic
  11.  
  12. @app.route("/get_jsonify")
  13. def get_jsonify():
  14. dic = {"name": "zbj", "age": 17}
  15. return jsonify(dic)
  16.  
  17. """
  18. 上面两种方式有什么不同呢?
  19. 1.使用json.dumps序列化后的字典,再返回的字符串,
  20. 它其实就是字符串,它的Content-Type: text/html;
  21. 浏览器并不知道你发的是json格式的字符串,
  22. 那么在前端就会认为你是普通的字符串,不能使用前端字典(Object)的方法(比如用 . 取值等)。
  23.  
  24. 2.使用jsonify返回的字典,它返回的是标准的json格式的字符串,
  25. 它的Content-Type: application/json;
  26. 浏览器现在是知道你发的是json格式的字符串,
  27. 那么在前端就会帮你进行处理成为前端的Object,可以使用前端Object的所有方法
  28. """
  29.  
  30. if __name__ == '__main__':
  31. app.run()

3.request.xxx

  1. from flask import Flask, request
  2.  
  3. app = Flask(__name__)
  4.  
  5. """
  6. flask中的request.args == Django中的request.GET
  7. 都是接收url中的参数
  8. """
  9.  
  10. @app.route("/get_args")
  11. def get_args():
  12. # 浏览器访问:http://127.0.0.1:5000/get_args?id=1&name=zbj
  13. print(request.method) # GET
  14. print(request.url) # http://127.0.0.1:5000/get_args?id=1&name=zbj
  15. print(request.cookies) # {}
  16. print(request.args) # ImmutableMultiDict([('id', '1'), ('name', 'zbj')])
  17. print(request.args.get("id")) # args本质上是一个字典,这里显示:1
  18. print(request.args["name"]) # zbj
  19. print(request.args.to_dict()) # {'id': '1', 'name': 'zbj'}
  20. return "hello"
  21.  
  22. if __name__ == '__main__':
  23. app.run()

4.request.form

  1. from flask import Flask, request
  2.  
  3. app = Flask(__name__)
  4.  
  5. @app.route("/get_form")
  6. def get_form():
  7. # 当前端页面提交了一个表单,表单内容是:username: zzbj
  8. # 使用request.form接收表单中提交的数据
  9. print(request.form) # ImmutableMultiDict([('username', 'zzbj')])
  10. print(request.form.get("username")) # zzbj
  11. print(request.form.to_dict()) # {"username": "zzbj"}
  12. return render_template("login.html")
  13.  
  14. if __name__ == '__main__':
  15. app.run()

3、配置文件

  1. 1. 配置文件信息
  2. flask中的配置文件是一个flask.config.Config对象(继承字典)
  3. 默认配置为:
  4. {
  5. 'ENV': production生产环境
  6. 'DEBUG': get_debug_flag(default=False), 是否开启Debug模式
  7. 'TESTING': False, 是否开启测试模式
  8. 'PROPAGATE_EXCEPTIONS': None,
  9. 'PRESERVE_CONTEXT_ON_EXCEPTION': None,
  10. 'SECRET_KEY': None, session是否加盐
  11. 'PERMANENT_SESSION_LIFETIME': timedelta(days=31),
  12. 'USE_X_SENDFILE': False,
  13. 'LOGGER_NAME': None, 日志名称
  14. 'LOGGER_HANDLER_POLICY': 'always',
  15. 'SERVER_NAME': None,
  16. 'APPLICATION_ROOT': None,
  17. 'SESSION_COOKIE_NAME': 'session',
  18. 'SESSION_COOKIE_DOMAIN': None,
  19. 'SESSION_COOKIE_PATH': None,
  20. 'SESSION_COOKIE_HTTPONLY': True,
  21. 'SESSION_COOKIE_SECURE': False,
  22. 'SESSION_REFRESH_EACH_REQUEST': True,
  23. 'MAX_CONTENT_LENGTH': None,
  24. 'SEND_FILE_MAX_AGE_DEFAULT': timedelta(hours=12),
  25. 'TRAP_BAD_REQUEST_ERRORS': False,
  26. 'TRAP_HTTP_EXCEPTIONS': False,
  27. 'EXPLAIN_TEMPLATE_LOADING': False,
  28. 'PREFERRED_URL_SCHEME': 'http',
  29. 'JSON_AS_ASCII': True,
  30. 'JSON_SORT_KEYS': True,
  31. 'JSONIFY_PRETTYPRINT_REGULAR': True,
  32. 'JSONIFY_MIMETYPE': 'application/json',
  33. 'TEMPLATES_AUTO_RELOAD': None,
  34. }
  35.  
  36. 2. 修改flask配置信息的方式
  37. Flask的配置信息是存在app.config里面的,
  38. 我们可以在实例化Flask对象之后,打印一下app.config来查看,
  39. app.config是所有配置信息组成的字典。
  40.  
  41. 方式一
  42. 既然是字典,就可以通过字典的方式进行修改:
  43. 通过app.config["xxx"] = xxx来更改配置信息,注意:config里面的键是大写的!!!
  44. 例如:
  45. app.config["DEBUG"] = True
  46. 注意:不建议这么使用
  47.  
  48. 方式二
  49. 可以直接通过Flask的实例化对象对配置进行修改,注意:这里是小写的
  50. app.debug = True
  51.  
  52. 方式三
  53. 通过app.config.from_object("python类或类的路径")来配置
  54.  
  55. 1.flask项目代码
  56. from flask import Flask
  57.  
  58. app = Flask(__name__)
  59. app.config.from_object('settings.DEVConfig')
  60. # app.config["DEBUG"] = True
  61. # app.debug = True
  62.  
  63. @app.route('/')
  64. def index():
  65. print(app.config)
  66. return "主页"
  67.  
  68. if __name__ == '__main__':
  69. app.run()
  70.  
  71. 2.settings.py
  72. class DEVConfig(object):
  73. DEBUG = True
  74.  
  75. class TestingConfig(object):
  76. TESTING = True

4、路由系统

  1. 1. 路由参数
  2. @app.route('/user/<username>') # 参数是字符串类型
  3. @app.route('/book/<int:id>') # 指定参数是int类型
  4. @app.route('/book/<float:id>') # 指定参数是float类型
  5. @app.route('/post/<path:path>')
  6. 注意:指定参数类型(intfloat等)的时候,冒号两边不能有空格,即int:id,不能是int: id
  7.  
  8. # 路由默认支持的参数
  9. DEFAULT_CONVERTERS = {
  10. 'default': UnicodeConverter, # 字符串
  11. 'string': UnicodeConverter, # 字符串
  12. 'any': AnyConverter, # 任意
  13. 'path': PathConverter, # 路径
  14. 'int': IntegerConverter, # 整数
  15. 'float': FloatConverter, # 小数
  16. 'uuid': UUIDConverter, # uuid
  17. }
  18.  
  19. 2. 路由的命名
  20. 通过给route设置endpoint参数,可以给路由进行命名,如果不设置endpoint,默认的路由命名是函数名
  21. @app.route('/book/<int:id>', endpoint='book') # 默认是函数名字
  22.  
  23. 3. 命名路由的反向解析
  24. from flask import Flask, url_for
  25. url_for("路由的名字", nid=32425)
  26.  
  27. 4. 例子
  28. from flask import Flask, redirect, url_for
  29.  
  30. app = Flask(__name__)
  31.  
  32. @app.route('/book/<int:id>', endpoint='book')
  33. def book(id):
  34. print(id, type(id))
  35. return "BOOK"
  36.  
  37. @app.route('/index', endpoint='index')
  38. def index():
  39. return redirect(url_for('book', id=1))
  40.  
  41. if __name__ == '__main__':
  42. 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. 示例

  1. from flask import Flask, render_template
  2.  
  3. app = Flask(__name__)
  4.  
  5. book_list = [
  6. {"id": 1, "title": "三国演义"},
  7. {"id": 2, "title": "水浒传"},
  8. {"id": 3, "title": "西游记"},
  9. {"id": 4, "title": "红楼梦"},
  10. ]
  11.  
  12. name = 'ZBJ的图书'
  13. my_dict = {"age": 18}
  14. my_list = [1, 2]
  15.  
  16. def my_func():
  17. return '<h2>把自定义函数传到模板</h2>'
  18.  
  19. @app.route('/book',)
  20. def book():
  21. return render_template('book.html', **{'book_list': book_list, 'name': name, 'my_dict': my_dict, 'my_list': my_list , 'my_func': my_func})
  22.  
  23. if __name__ == '__main__':
  24. app.run()

python代码

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <h1>{{name}}</h1>
  9. <table>
  10. <thead>
  11. <tr>
  12. <th>title</th>
  13. <th>操作</th>
  14. </tr>
  15. </thead>
  16. <tbody>
  17. {% for book in book_list %}
  18. <tr>
  19. <td>{{book.title}}</td>
  20. <td>删除 | 编辑</td>
  21. </tr>
  22. {% endfor %}
  23. </tbody>
  24.  
  25. </table>
  26. <hr>
  27. {{my_func()|safe}}
  28. <hr>
  29. 字典get取值:{{my_dict.get("age", 0)}}
  30. <br>
  31. 字典 . 点取值:{{my_dict.age}}
  32. <br>
  33. <hr>
  34. 列表索引取值:{{my_list[0]}}
  35. <br>
  36. 列表 . 点取值:{{my_list.1}}
  37. <hr>
  38. </body>
  39. </html>

模板代码

6、请求和响应

  1. from flask import Flask
  2. from flask import request
  3. from flask import render_template
  4. from flask import redirect
  5. from flask import make_response
  6.  
  7. app = Flask(__name__)
  8.  
  9. @app.route('/login', methods=['GET', "POST"])
  10. def login():
  11. """
  12. 1、请求相关信息
  13. request.method 请求的方式
  14. request.args 请求的url的参数 ?
  15. request.form form表单的数据
  16. request.values
  17. request.cookies cookies
  18. request.headers 请求头
  19. request.path 请求的url路径(不含参数 ?)
  20. request.full_path 请求的url路径(包含参数 ?)
  21. request.script_root 脚本的地址
  22. request.url
  23. request.base_url
  24. request.url_root
  25. request.host_url
  26. request.host
  27. request.files 上传过来的文件数据
  28. 文件的一些使用方法
  29. obj = request.files['the_file_name'] 获取文件名
  30. obj.save('/uploads/' + secure_filename(f.filename)) 把文件数据保存在服务器
  31.  
  32. 2、响应相关信息
  33. 1. 这几种是把响应直接放在响应体里面,然后直接返回
  34. return "字符串"
  35. return render_template('html模板路径',**{})
  36. return redirect('/index.html')
  37.  
  38. 2. 下面这种是先封装响应对象,然后对响应对象做一些操作,再把响应对象返回
  39. response = make_response(render_template('index.html')) response是flask.wrappers.Response类型
  40. response.delete_cookie('key') 删除cookie
  41. response.set_cookie('key', 'value') 设置cookie
  42. response.headers['X-Something'] = 'value' 设置响应头
  43. return response 最后返回
  44. """
  45. return "看注释吧!"
  46.  
  47. if __name__ == '__main__':
  48. 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代码

  1. # python代码
  2. from flask import Flask, session, redirect, url_for, request, render_template
  3. # 注意:导入的时候session跟sessions是不同的,注意 s
  4.  
  5. app = Flask(__name__)
  6. # session必须设置session加密的盐
  7. app.secret_key = '嘿嘿嘿'
  8.  
  9. @app.route('/login', methods=['GET', 'POST'])
  10. def login():
  11. if request.method == "POST":
  12. username = request.form.get('username')
  13. pwd = request.form.get('pwd')
  14. if username == 'xiaoming' and pwd == '':
  15. session['username'] = username
  16. return redirect(url_for('index'))
  17. return render_template('login.html', error="用户名或者密码错误")
  18. return render_template('login.html')
  19.  
  20. @app.route('/index')
  21. def index():
  22. if session.get('username', ''):
  23. return "用户已登录,这是首页"
  24. return "用户未登录,请去登录页面"
  25.  
  26. @app.route('/logout')
  27. def logout():
  28. session.pop('username', None)
  29. return redirect(url_for('index'))
  30.  
  31. if __name__ == '__main__':
  32. app.run()

2. login.html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8.  
  9. <form action="" method="post">
  10.  
  11. <input type="text" name="username">
  12. <input type="text" name="pwd">
  13. <input type="submit">
  14. <p>{{error}}</p>
  15. </form>
  16.  
  17. </body>
  18. </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

  1. from flask import Flask, session
  2.  
  3. app = Flask(__name__)
  4. app.secret_key = '雅蠛蝶'
  5.  
  6. @app.route('/set')
  7. def set():
  8. session['name'] = '小明'
  9. return "SET"
  10.  
  11. @app.route('/get')
  12. def get():
  13. name = session.pop('name')
  14. print(name)
  15. return "GET"
  16.  
  17. if __name__ == '__main__':
  18. app.run()
  19.  
  20. 取了一次值后,下次去get,就取不到值了

2. flashDemo

  1. from flask import Flask, flash, get_flashed_messages
  2.  
  3. app = Flask(__name__)
  4. app.config['SECRET_KEY'] = '雅蠛蝶'
  5.  
  6. @app.route('/set')
  7. def set_flash():
  8. flash('小明', 'ming')
  9. flash('小东')
  10. return "SET_FLASH"
  11.  
  12. @app.route('/get')
  13. def get_flash():
  14. name1 = get_flashed_messages()
  15. print(name1) # ['小明', '小东']
  16. name2 = get_flashed_messages(category_filter=['ming'])
  17. print(name2) # ['小明']
  18. return "GET_FLASH"
  19.  
  20. if __name__ == '__main__':
  21. app.run()

9、中间件

9-1、小预习
Flask的中间件跟Django的不太一样,我们需要从源码去理解,
需要用到类的一些内置方法(双下方法):__init__、__call__

1. 类

  1. class A():
  2. def __init__(self):
  3. print("init")
  4.  
  5. def __call__(self, *args, **kwargs):
  6. print("call")
  7.  
  8. a = A() # init
  9. a() # call
  10. 类加括号:实例化一个对象-->调用__init__方法
  11. 对象加括号:调用__call__方法

2. werkzeug启动flask项目的原理

  1. from werkzeug.wrappers import Request, Response
  2. from werkzeug.serving import run_simple
  3.  
  4. @Request.application
  5. def hello(request):
  6. return Response('Hello World!')
  7.  
  8. if __name__ == '__main__':
  9. run_simple('localhost', 5000, hello)
  10.  
  11. 最后启动项目的是执行了run_simple这个方法,
  12. 这个方法有三个参数:
  13. localhost:域名
  14. 5000:端口
  15. hello:函数名
  16.  
  17. 意思就是:在这个域名这个端口,调用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. 改源码(不建议使用)

  1. def __call__(self, environ, start_response):
  2. """The WSGI server calls the Flask application object as the
  3. WSGI application. This calls :meth:`wsgi_app` which can be
  4. wrapped to applying middleware."""
  5. # return self.wsgi_app(environ, start_response)
  6. print("开始之前")
  7. ret = self.wsgi_app(environ, start_response)
  8. print("请求之后")
  9. return ret

2. 中间件类

  1. from flask import Flask
  2.  
  3. app = Flask(__name__)
  4.  
  5. class Middleware(object):
  6. def __init__(self, old_call):
  7. self.old_call = old_call
  8.  
  9. def __call__(self, *args, **kwargs):
  10. print('请求来之前做的事情')
  11. ret = self.old_call(*args, **kwargs)
  12. print('请求来到之后做的事情')
  13. return ret
  14.  
  15. app.wsgi_app = Middleware(app.wsgi_app)
  16.  
  17. if __name__ == '__main__':
  18. app.run()
  19. # run_simple(host, port, self, **options)
  20. # run_simple执行了 self()
  21. # self = app
  22. # app() 执行Flask的__call__
  23. # __call__返回app.wsgi_app()的执行结果
  24. # 我重写了app.wsgi_app--> Middleware(app.wsgi_app)()
  25.  
  26. 注意:通常我们不会这样去实现中间件,因为Flask中有一些特殊的装饰器能够帮我们实现中间件的功能,哈哈哈哈~~~

10、特殊的装饰器

需求:
  我们很多访问都是需要进行登录认证,那么在flask中如何实现登录认证的功能

1. 小预习

  1. # python的装饰器
  2. from functools import wraps
  3. # 被装饰器装饰的函数名字会变成内部的函数名
  4. # 我们需要给inner函数加个装饰器来修改inner函数的信息
  5.  
  6. def wrapper(func):
  7. @wraps(func)
  8. def inner(*args, **kwargs):
  9. ret = func(*args, **kwargs)
  10. return ret
  11. return inner
  12.  
  13. @wrapper
  14. def index(x, y):
  15. return x + y
  16.  
  17. res = index(1, 2)
  18. print(res)
  19. print(index.__name__)

2. 使用python装饰器实现认证功能

  1. 给每个需要认证的函数都加上自定义的认证装饰器
  2.  
  3. from flask import Flask, session, redirect, url_for
  4. from functools import wraps
  5.  
  6. app = Flask(__name__)
  7. app.secret_key = '哈哈'
  8.  
  9. # 实现登录 我们不可能每个视图函数都去获取session然后判断
  10. # 我们可以利用装饰器
  11. # 注意装饰器装饰完函数名的问题
  12. # 注意装饰器执行顺序问题
  13.  
  14. # 实现认证的装饰器
  15. def auth(func):
  16. @wraps(func)
  17. def inner(*args, **kwargs):
  18. if not session.get('username'):
  19. return redirect(url_for('login'))
  20. ret = func(*args, **kwargs)
  21. return ret
  22. return inner
  23.  
  24. @app.route('/login')
  25. def login():
  26. session['username'] = '小明'
  27. return "登录成功"
  28.  
  29. @app.route('/logout')
  30. def logout():
  31. session.pop('username')
  32. return "退出登录"
  33.  
  34. # 注意:app.route和auth装饰器的顺序不能乱,必须是先认证了成功后才放行
  35. @app.route('/user')
  36. @auth
  37. def user():
  38. return "用户页面"
  39.  
  40. @app.route('/book')
  41. @auth
  42. def book():
  43. return "图书页面"
  44.  
  45. if __name__ == '__main__':
  46. app.run()

3. Flask提供的特殊装饰器before_request
任何请求进来,都要先执行被before_request装饰的函数,
在这个函数进行认证,其他函数就不需要每个都加认证装饰器了。

  1. from flask import Flask, session, redirect, url_for, request
  2.  
  3. app = Flask(__name__)
  4. app.secret_key = '哈哈'
  5.  
  6. # 任何请求进来,都要先执行这个函数
  7. @app.before_request
  8. def auth():
  9. if request.path == '/login' or request.path == '/':
  10. # 如果是登录请求,直接放行
  11. return None
  12. if session.get('username', ''):
  13. # 如果携带了session的请求,证明已经登录,放行
  14. return None
  15. # 否则,都跳转到登录页面
  16. return redirect(url_for('login'))
  17.  
  18. @app.route('/login')
  19. def login():
  20. session['username'] = '小明'
  21. return "登录成功"
  22.  
  23. @app.route('/logout')
  24. def logout():
  25. session.pop('username')
  26. return "退出登录"
  27.  
  28. # 不需要加其他装饰器了
  29. @app.route('/user')
  30. def user():
  31. return "用户页面"
  32.  
  33. @app.route('/book')
  34. def book():
  35. return "图书页面"
  36.  
  37. if __name__ == '__main__':
  38. app.run()

11、flask常用的特殊装饰器

  1. 1. 请求第一次进来的时候执行
  2. @app.before_first_request
  3. 第二次请求就不会执行了
  4.  
  5. 2. 请求每次进来都执行
  6. @app.before_request
  7.  
  8. 3. 每次响应的时候执行
  9. @app.after_request
  10.  
  11. 4. 发生404的时候执行
  12. @app.errorhandler(404)
  13.  
  14. 5. 全局的函数,在所有html模板中都可以使用的函数,并且不需要传
  15. @app.template_global()
  16.  
  17. 6.模板的过滤器,在所有html模板中都可以使用的过滤器,并且不需要传
  18. @app.template_filter()

7. Demo

  1. from flask import Flask, Request, render_template
  2.  
  3. app = Flask(__name__)
  4. app.debug = True
  5.  
  6. @app.before_first_request
  7. def before_first_request1():
  8. print('before_first_request1')
  9.  
  10. @app.before_first_request
  11. def before_first_request2():
  12. print('before_first_request2')
  13.  
  14. @app.before_request
  15. def before_request1():
  16. print('before_request1')
  17.  
  18. @app.before_request
  19. def before_request2():
  20. print('before_request2')
  21.  
  22. @app.after_request
  23. def after_request1(response):
  24. print('after_request1', response)
  25. return response
  26.  
  27. @app.after_request
  28. def after_request2(response):
  29. print('after_request2', response)
  30. return response
  31.  
  32. @app.errorhandler(404)
  33. def page_not_found(error):
  34. return 'This page does not exist', 404
  35.  
  36. @app.template_global()
  37. def jiafa(a1, a2):
  38. # 在所有模板在都可以这样使用 {{ jiafa(1, 2) }}
  39. return a1 + a2
  40.  
  41. @app.template_filter()
  42. def db(data):
  43. # 在所有模板在都可以这样使用 {{ "hello"|db() }}
  44. # 这里data就是hello
  45. return data[::2]
  46.  
  47. @app.route('/')
  48. def hello_world():
  49. return render_template('hello.html')
  50.  
  51. if __name__ == '__main__':
  52. app.run()

Demo

8. 注意
这几个装饰器跟Django的中间件很像,也有执行顺序的
request请求进来,顺序执行
response响应出去,倒序执行

超级注意
  !!! before_request有返回值的时候还会按顺序执行after_request
  !!! Django<=1.9的版本 当process_request有返回值的时候跟flask是一样的

9. 请求流程图

12、CBV视图

1. route原理

  1. 之前我们的视图都是FBV,那么我们的CBV要怎么实现呢?
  2. 实现CBV之前我们要看一下路由的实现原理
  3. 我们看下@app.route("/xx")做了什么:
  4.  
  5. 首先这是一个带参数的装饰器,那么带参数的装饰器执行顺序是什么样的
  6. 1,先去掉@ 执行route("/xx")得到返回值
  7. 2,再拿返回值加上@符号去装饰接下来的函数
  8.  
  9. route源码
  10. def route(self, rule, **options):
  11. def decorator(f):
  12. endpoint = options.pop('endpoint', None)
  13. self.add_url_rule(rule, endpoint, f, **options)
  14. return f
  15. return decorator
  16.  
  17. 从源码中可以看出:
  18. route函数返回了decorator函数,也就是说实际上是用decorator这个函数去装饰我们的视图函数的
  19. f就是我们的视图函数,
  20. decorator调用了add_url_rule方法
  21. endpoint默认取函数名!
  22. 两个函数不能用一个endpoint
  23.  
  24. 那么实际上路由的实现原理:
  25. from flask import Flask
  26.  
  27. app = Flask(__name__)
  28.  
  29. def index():
  30. return "index"
  31.  
  32. app.add_url_rule("/", endpoint="index", view_func=index)
  33.  
  34. if __name__ == '__main__':
  35. app.run()

2. CBV

  1. # CBV需要导入views
  2. from flask import Flask, views, session, redirect, url_for
  3. from functools import wraps
  4.  
  5. app = Flask(__name__)
  6.  
  7. def auth(func):
  8. @wraps(func)
  9. def inner(*args, **kwargs):
  10. if not session.get("userinfo"):
  11. return "你还没登录,滚"
  12. ret = func(*args, **kwargs)
  13. return ret
  14. return inner
  15.  
  16. class MyView(views.MethodView):
  17. methods = ["GET", "POST"]
  18. # 设置decorators相当于给这个CBV的所有请求方法都加了指定的装饰器
  19. # decorators = [auth, ]
  20.  
  21. def get(self):
  22. return "GET"
  23.  
  24. def post(self):
  25. return "POST"
  26.  
  27. # 第一个参数:路由
  28. # 第二个参数:视图
  29. # name == endpoint 命名路由
  30. app.add_url_rule("/index", view_func=MyView.as_view(name="index"))
  31.  
  32. if __name__ == '__main__':
  33. app.run()

13、自定义路由的正则匹配

  1. from flask import Flask, url_for
  2. from werkzeug.routing import BaseConverter
  3.  
  4. app = Flask(__name__)
  5.  
  6. class RegexConverter(BaseConverter):
  7. """
  8. 自定义URL匹配正则表达式
  9. """
  10.  
  11. def __init__(self, map, regex):
  12. super(RegexConverter, self).__init__(map)
  13. self.regex = regex
  14.  
  15. def to_python(self, value):
  16. """
  17. 路由匹配时,匹配成功后传递给视图函数中参数的值
  18. value默认的类型是str,我们可以设置成我们想要的类型
  19. :param value:
  20. :return:
  21. """
  22. return int(value)
  23.  
  24. def to_url(self, value):
  25. """
  26. 使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数
  27. :param value:
  28. :return:
  29. """
  30. val = super(RegexConverter, self).to_url(value)
  31. return val
  32.  
  33. # 新加一个匹配规则regex到flask中
  34. # 原本只有 int string等,现在还多了一个regex
  35. app.url_map.converters['regex'] = RegexConverter
  36.  
  37. @app.route('/index/<regex("\d+"):nid>')
  38. def index(nid):
  39. print(url_for('index', nid=''))
  40. print(nid, type(nid))
  41. return 'Index'
  42.  
  43. if __name__ == '__main__':
  44. app.run()

14、蓝图

1. 作用
为我们提供目录的划分,就是解耦用的。

2. 目录

3. 实现步骤

  1. 1,新建一个项目目录 项目目录下建一个同名的python
  2. 2,在这个包同级下建manager.py
  3. 3, 在包的__init__ 实例化Flask对象
  4. 4,在manager.py 导入Flask的实例化对象app app.run()
  5. 5, Python包里建立views文件夹
  6. 任何一个文件都都可以生成蓝图对象
  7. from flask import BluePrint
  8. # 实例化蓝图对象
  9. userBlue = BluePrint("userBlue", __name__)
  10. @userBlue.route("/user")
  11. def user():
  12. return "xxx"
  13. 6, 把蓝图对象注册到app
  14. app.register_blueprint(bookBlue, **options)

4. 注意
每个蓝图可以指定自己的模板以及静态文件~

还可以在app中注册蓝图的时候指定路由前缀~~

还可以给蓝图加before_request~~

5. 示例代码

  1. from flask import Flask
  2. from .views.book import bookBlue
  3. from .views.user import userBlue
  4.  
  5. # 创建Flask的实例化对象app
  6. def create_app():
  7. app = Flask(__name__, template_folder='templates') # 指定templates
  8. app.register_blueprint(userBlue) # 把蓝图对象注册到app
  9. app.register_blueprint(bookBlue, url_prefix='/book') # 还可以指定路由的前缀
  10. return app

__init__.py

  1. from BlueDemo import create_app
  2.  
  3. # 导入Flask的实例化对象app
  4. app = create_app()
  5.  
  6. if __name__ == '__main__':
  7. # 启动Flask
  8. app.run()

manage.py

  1. from flask import Blueprint, render_template
  2.  
  3. # 实例化一个蓝图对象
  4. userBlue = Blueprint('userBlue', __name__)
  5.  
  6. @userBlue.route('/user')
  7. def user():
  8. return render_template('user.html')

views/user.py

  1. from flask import Blueprint
  2.  
  3. # 实例化一个蓝图对象
  4. bookBlue = Blueprint('bookBlue', __name__)
  5.  
  6. # bookBlue蓝图对象,注册到app的时候,指定了url_prefix,相当于做了路由的分发
  7. # 这个视图的路由:/book/list
  8. @bookBlue.route('/list')
  9. def list():
  10. return "Book_list"
  11.  
  12. # 这个视图的路由:/book/create
  13. @bookBlue.route('/create')
  14. def create():
  15. return "Book_create"

views/book.py

15、CBV配合蓝图

  1. # CBV需要导入views
  2. from flask import Flask, Blueprint, views, url_for, session
  3. from functools import wraps
  4.  
  5. app = Flask(__name__) # Flask实例
  6. userBlue = Blueprint("userBlue", __name__) # 蓝图实例
  7.  
  8. # 登录验证的装饰器
  9. def auth(func):
  10. @wraps(func)
  11. def inner(*args, **kwargs):
  12. if not session.get("userinfo"):
  13. return "你还没登录,滚"
  14. ret = func(*args, **kwargs)
  15. return ret
  16. return inner
  17.  
  18. # CBV类
  19. class Home(views.MethodView):
  20. # 默认写出来的请求方法都被允许,如果还是想重定制,可使用methods属性来重新定义允许的请求
  21. methods = ["GET", "POST"]
  22.  
  23. # 设置decorators相当于给这个CBV的所有请求方法都加了指定的装饰器,装饰器按索引顺序执行
  24. decorators = [auth, ]
  25.  
  26. def dispatch_request(self, *args, **kwargs):
  27. """可以写一些自己的逻辑"""
  28. return super(Home, self).dispatch_request(*args, **kwargs)
  29.  
  30. def get(self):
  31. return "GET"
  32.  
  33. def post(self):
  34. return "POST"
  35.  
  36. # dispatch_request方法在父类views.MethodView是已经定义了的
  37. # 是用于根据请求的不同进入不同的方法,例如我们对上面的CBV进行get请求,那么dispatch_request方法就会把请求分配进入get()方法
  38. # 而不需要我们自己手动进行 request==GET 的判断。
  39. # 因此可以重写dispatch_request方法可以在里面先实现一些我们的逻辑,再调用父类的dispatch,当然也可以不重写
  40.  
  41. # 第一个参数:路由
  42. # 第二个参数:视图(类.as_view())
  43. # name == endpoint 命名路由
  44. userBlue.add_url_rule("/index", view_func=Home.as_view(name="index")) # 在蓝图中定义路由
  45. app.register_blueprint(userBlue) # 把蓝图注册到app
  46.  
  47. # 注意:当一个蓝图被声明后,如果要使用url_for进行自定寻址需要加上蓝图名称
  48. # 即:url_for(blue_name.endpoint)
  49. # 例如,上面的"/index"路由,as_view后的第一个参数(name)就是endpoint
  50. url_for("userBlue.index")
  51.  
  52. if __name__ == '__main__':
  53. app.run()

16、蓝图的templates和项目的templates

1、首先看一下我定义的蓝图结构(使用的是CBV模式)

说明

蓝图会根据蓝图的template_folder参数去找模板,但是需要注意的是:

  1. 首先都是在项目级别的模板中查找,如果项目级别的templates没有我们视图指定的html文件,那么再去找蓝图级别的templates
  2. 如果有两个蓝图或多个蓝图,它们的模板目录结构一样,如果在它们的templates下也有着同名html文件,
    那么后面的蓝图加载的同名html文件会是第一个蓝图中的html。

  3. 蓝图的templates要跟实例化蓝图的py文件在同一级,否则找html文件的时候会找不到,因为蓝图查找html文件是从实例化蓝图的地方开始,向下查找
  4. 即我上面的结构中,蓝图的实例化在home_url.py,那么蓝图的templates就要建立在与home_url.py同一级的地方

 2、示例代码

项目的__init__.py

  1. from flask import Flask
  2. from flask_session import Session
  3. from flask_demo.home.home_url import homeBlue
  4.  
  5. def create_app():
  6. app = Flask(__name__)
  7. app.config.from_object('setting.DevConfig')
  8. Session(app)
  9. app.register_blueprint(homeBlue)
  10. return app

__init__.py

  1. from flask_demo import create_app
  2.  
  3. app = create_app()
  4.  
  5. if __name__ == '__main__':
  6. app.run()

manager.py

  1. from flask import views, session, render_template
  2.  
  3. class Home(views.MethodView):
  4. methods = ["GET"]
  5.  
  6. def get(self):
  7. session['login'] = 'login'
  8. return render_template('home.html')
  9.  
  10. class GetSession(views.MethodView):
  11. def get(self):
  12. resp = session.get('login')
  13. return resp

flask_demo/home/views/home.py

  1. from flask import Blueprint
  2. from flask_demo.home.views.home import Home, GetSession
  3.  
  4. homeBlue = Blueprint('homeBlue', __name__, template_folder='templates')
  5.  
  6. homeBlue.add_url_rule('/home', view_func=Home.as_view('home'))
  7. 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初识的更多相关文章

  1. python 全栈开发,Day119(Flask初识,Render Redirect HttpResponse,request,模板语言 Jinja2,用户登录例子,内置Session)

    一.Flask初识 首先,要看你学没学过Django 如果学过Django 的同学,请从头看到尾,如果没有学过Django的同学,并且不想学习Django的同学,轻饶过第一部分 三大主流Web框架对比 ...

  2. 第一篇 Flask初识

    一. Python 现阶段三大主流Web框架 Django Tornado Flask 对比 1.Django 主要特点是大而全,集成了很多组件,例如: Models Admin Form 等等, 不 ...

  3. Flask初识之安装及HelloWord程序

    Python 现阶段三大主流Web框架 Django Tornado Flask 对比 1.Django 主要特点是大而全,集成了很多组件,例如: Models Admin Form 等等, 不管你用 ...

  4. flask学习之路

    目录 flask初识 flask模板 flask的session flask路由 flask配置和实例化传参 flask蓝图 更新中

  5. Flask&&人工智能AI --1

    Flask初识,Response三剑客,jsonify以及send_file.Request,模板语言 Jinja2,用户登录例子,内置Sessio 一.Flask初识 首先,要看你学没学过Djang ...

  6. python全栈开发day110-Flask基础语法

    1.Flask 初识: 短小精悍,三方支持的组件多 稳定性较差 2.三行 :启动flask服务 from flask import Flask app = Flask(__name__) app.ru ...

  7. flask 第七章 简陋版智能玩具 +MongoDB初识和基本操作

    1.简陋版web智能玩具 FAQ.py文件 import os from aip import AipSpeech, AipNlp from uuid import uuid4 "" ...

  8. Flask(1)- 主流web框架、初识flask

    一.Python 现阶段三大主流Web框架 Django.Tornado.Flask 对比 Django 主要特点是大而全,集成了很多组件(例如Models.Admin.Form等等), 不管你用得到 ...

  9. Flask&&人工智能AI -- 8 HTML5+ 初识,HBuilder,夜神模拟器,Webview

    昨日内容回顾 1.增删改查: 增: db.collections.insert({a:1}) // 官方不推荐了 db.collections.insertMany([{a:1},{b:1}]) in ...

随机推荐

  1. Linux设备驱动之IIO子系统——IIO框架数据读取

    IIO DATA ACCESS IIO数据获取 只有两种方法可以使用IIO框架访问数据; 通过sysf通道进行一次性捕获,或通过IIO字符设备进行连续模式(触发缓冲). One-shot captur ...

  2. Django 笔记分享

    Django是一个基于MVC构造的框架.但是在Django中,控制器接受用户输入的部分由框架自行处理,所以 Django 里更关注的是模型(Model).模板(Template)和视图(Views), ...

  3. Object.keys 及表单清空

    Object.keys 返回一个所有元素为字符串的数组,其元素来自于从给定的object上面可直接枚举的属性.这些属性的顺序与手动遍历该对象属性时的一致. // simple array var ar ...

  4. thymeleaf的配置

    1.在springboto项目中使用thymeleaf标签,必须先添加依赖,如下. <dependency> <groupId>org.springframework.boot ...

  5. Dynamics 365执行操作报SQL Server已超时,更改这个超时设置的方法

    本人微信公众号:微软动态CRM专家罗勇 ,回复291或者20190110可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me!我的网站是 www.luoyong.me . 当执 ...

  6. nginx代理天地图做缓存解决跨域问题

    作为一个GISer开发者,天地图是经常在项目中以底图的形式出现,其加载地址如: 天地图矢量:http://t{0-6}.tianditu.com/DataServer?T=vec_w&x={x ...

  7. ASP.NET Core 入门教程 5、ASP.NET Core MVC 视图传值入门

    一.前言 1.本教程主要内容 ASP.NET Core MVC 视图引擎(Razor)简介 ASP.NET Core MVC 视图(Razor)ViewData使用示例 ASP.NET Core MV ...

  8. Oracle database link中查询会开启事务吗?

    关于oracle database link,使用database link相关的查询语句是否会开启事务呢?我们知道,在数据库中一个简单的SELECT查询语句不会产生事务(select for upd ...

  9. 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权限,添加即可,如果已加域则要在域策略修改,或退域安装后在加域.

  10. Mysql数据中Packet for query is too large错误的解决方法

    有时,程序在连接mysql执行操作数据库时,会出现如下类似错误信息: Packet for query is too large (4230 > 1024). You can change th ...