• 浅谈 Flask 框架


    一、框架对比

    • Django —— 教科书式框架

      • 优势:组件全,功能全,教科书

      • 劣势:占用资源,创建复杂度高

    • Flask —— 以简单为基准开发,一切从简,能省则省

      • 优势:轻,块

      • 劣势:先天不足,第三方组件稳定性较差

    DjangoFlask
    Admin —— Model 原生无
    Model 原生无
    Form 原生无
    Session 有 —— 颠覆认知操作

    二、Flask入门

    • 下载安装

      • 下载:pip install Flask

      • 注意:不要使用工具中的插件创建 Flask 项目

    • 三行代码启动Flask项目

      from flask import Flask
      app = Flask(__name__)
      app.run()

    三、Flask的请求和响应

    3.1 Flask中的响应Response

    • 与django对比

      DjangoFlask返回
      HttpResponse("hello") "hello" 字符串
      render(request,"html",{"k":v}) render_template("html",k=v) html页面
      redirect("/") redirect("/") 重定向
      JsonResponse({"k":v}) jsonify({"k":v}) 标准的json字符串
      • 以上是web框架的Response三剑客

    • 返回字符串 —— string

      from flask import Flask
      app = Flask(__name__)
      @app.route("/")
      def home():
          return "hello World I am Flask"
      app.run()
    • 返回html页面 —— string

      from flask import Flask, render_template
      app = Flask(__name__)
      @app.route("/index")
      def index():
          return render_template("index.html")
      app.run()
    • 返回重定向 —— string

      from flask import Flask, redirect
      app = Flask(__name__)
      @app.route("/login")
      def login():
          return redirect("/index")
      app.run()
    • 返回文件内容 —— Response instance

      • send_file():读取并返回文件内容,自动识别文件类型,Content-type中添加类型,即,Content-type:文本类型

      • 浏览器特性:

        • 可识别的Content-type,会自动渲染

        • 不可识别的Content-type,会自动下载

      from flask import Flask, send_file
      app = Flask(__name__)
      @app.route("/get_file")
      def get_file():
          # 返回文件内容,自动识别文件类型,Content-type中添加文件类型,Content-type:文件类型
          return send_file("2.exe")
      app.run()
    • 返回标准格式的json字符串 —— string

      • jsonify():返回标准格式的json字符串,本质:先序列化json的字典,Content-type中加入Application/json,Flask1.1.1 直接返回字典格式,相当于自动执行jsonify

      from flask import Flask, send_file
      app = Flask(__name__)
      @app.route("/get_json")
      def get_json():
          d = {"k":"v"}
          # 返回标准Json格式字符串 API接口
          return jsonify(d)
      app.run()

    3.2 Flask中的请求Request

    • 与django对比

      DjangoFlaskFlask中的含义
      request.method request.method 获取请求方式
      request.GET request.args 获取URL中的数据
      request.POST request.form 获取FormData中的数据 ,也就是所谓的Form标签
      request.FILES request.files 获取request中的文件,返回FileStorage中,存在save(保存文件)和filename(原始文件名)
        request.json 请求中Content-Type:application/json,请求体中的数据被序列化到 request.json中,以字典的形式存放
        request.data 请求中Content-Type中不包含Form或FormData,保留请求体中的原始数据,bytes类型
      request.path_info request.path 请求路径 路由地址
      request.get_full_patch() request.url 访问请求的完整路径包括 url参数
      request.get_host() request.host 主机位 127.0.0.1:5000
      request.COOKTES request.cookies 字典获取浏览器请求时带上的Cookie
      • 注意:request.values,获取url和FormData中的数据,敏感地带(url和FormData中存在相同的key时会出错)

    import os
    from flask import Flask, request, render_template
    # request 请求上下文管理
    app = Flask(__name__)
    ​
    @app.route("/login",methods=["GET","POST"])
    def login():
        # print(request.args.to_dict())
        # print(request.host)
        # print(request.path)
        # print(request.url)
        # print(request.cookies)
        # 优先判断 请求方式
        # 如果是 GET 请求 返回登录页面
        if request.method == "GET":
            return render_template("login.html")
        # 如果是 POST 请求 获取用户名密码 校验
        else: # 405 请求方式不被允许
            my_file = request.files.get("my_file")
            filename = my_file.filename # 获取原始文件名
            file_path = os.path.join("avatar",filename)
            my_file.save(file_path)
            print(request.form.to_dict()) # form - FormData
            if request.form.get("username") == "YWB":
                return "Login OK!"
        return "Login 不OK!"if __name__ == '__main__':
        app.run()
    示例

    四、 Jinja2:template语言

    • {{ }} 引用或执行

    • {% %} 逻辑引用

      STUDENT = {'name': 'Old', 'age': 38, 'gender': ''}
      STUDENT_LIST = [
          {'name': 'Old', 'age': 38, 'gender': ''},
          {'name': 'Boy', 'age': 73, 'gender': ''},
          {'name': 'EDU', 'age': 84, 'gender': ''}
      ]
      STUDENT_DICT = {
          1: {'name': 'Old', 'age': 38, 'gender': ''},
          2: {'name': 'Boy', 'age': 73, 'gender': ''},
          3: {'name': 'EDU', 'age': 84, 'gender': ''},
      }
      from flask import Flask, render_template, Markup
      app = Flask(__name__)
      ​
      @app.template_global()
      def ab(a,b):
          return a+b
      ​
      @app.route("/")
      def home():
          return render_template("stu.html",stu=STUDENT,stu_l = STUDENT_LIST,stu_d = STUDENT_DICT)
      ​
      @app.template_global()
      def my_input(na,ty):
          s = f"<input type='{ty}' value='{na}'>"
          return Markup(s)
      ​
      if __name__ == '__main__':
          app.run()
      <p>{{ ab(50,60) }}</p>
      
      {{ stu }}
      <table border="1px">
          <tr>
              <td>name</td>
              <td>age</td>
              <td>gender</td>
          </tr>
          <tr>
              <td>{{ stu.name }}</td>
              <td>{{ stu["age"] }}</td>
              <td>{{ stu.get("gender") }}</td>
          </tr>
      </table>
      
      {{ stu_l }}
      <table border="1px">
          <tr>
              <td>name</td>
              <td>age</td>
              <td>gender</td>
          </tr>
          {% for student in stu_l %}
          <tr>
              <td>{{ student.name }}</td>
              <td>{{ student["age"] }}</td>
              <td>
                  {% if  student.get("gender") != "" and  student.get("gender") != "" %}
                  女
                  {% else %}
                  {{ student.get("gender") }}
                  {% endif %}
              </td>
          </tr>
          {% endfor %}
      </table>
      
      {{ stu_d }}
      <table border="1px">
          <tr>
              <td>id</td>
              <td>name</td>
              <td>age</td>
              <td>gender</td>
          </tr>
          {% for skey,svalue in stu_d.items() %}
          <tr>
              <td>{{ skey }}</td>
              <td>{{ svalue.get("name") }}</td>
              <td>{{ svalue.get("age") }}</td>
              <td>
                  11111
              </td>
          </tr>
          {% endfor %}
      </table>
      stu.html

    五、Flask的知识点

    5.1 Flask中的session

    • 基于请求上下文管理机制

      STUDENT = {'name': 'Old', 'age': 38, 'gender': ''}
      STUDENT_LIST = [
          {'name': 'Old', 'age': 38, 'gender': ''},
          {'name': 'Boy', 'age': 73, 'gender': ''},
          {'name': 'EDU', 'age': 84, 'gender': ''}
      ]
      STUDENT_DICT = {
          1: {'name': 'Old', 'age': 38, 'gender': ''},
          2: {'name': 'Boy', 'age': 73, 'gender': ''},
          3: {'name': 'EDU', 'age': 84, 'gender': ''},
      }
      from flask import Flask, render_template, request, session, redirect
      app = Flask(__name__)
      # app.config["DEBUG"] = True
      app.debug = True
      app.secret_key = "!@#$%^(*&^%$#@#$%&*(^$WQ*(^EWET*^EWEU"
      
      def war(func): # func = home
          def inner(*args,**kwargs):
              # 校验session
              if session.get("user"):
                  ret = func(*args,**kwargs) # func = home
                  return ret
              else:
                  return redirect("/login")
          return inner
      
      @app.route("/")
      @war           # war(home) -> inner == == home
      # 先要经过一次校验 才能执行或不执行视图函数
      def home():
          # 校验用户登录 session
          #如果session。get(“user”) 存在
          # 如果不存在 返回Login
          return render_template("stu.html",stu=STUDENT,stu_l = STUDENT_LIST,stu_d = STUDENT_DICT)
      
      @app.route("/a")
      def homea():
          # 校验用户登录状态?
          # 校验session中有没有 user Key
          if session.get("user"):
              inp = Markup("<input type='submit' value='后端给返回的按钮'>")
              return render_template("my_a.html", btn=inp)
          # 校验失败
          # 跳转login
          else:
              return redirect("/login")
      
      @app.route("/login",methods=["GET","POST"])
      def login():
          # 当请求方式为 GET 的时候
          if request.method == "GET":
              # eyJ1c2VyIjoiYWxleGFuZGVyLmRzYi5saSJ9.XUfYMw.o8oZX2GMC-kjZPWBkyRuUeSMp4M
              #  交由客户端保管机制
              # {"user":"alexander.dsb.li"}
            # Flask理念 - 一切从简 为服务器减轻压力
              return render_template("login.html")
          else:
              user_info = request.form.to_dict()
              session["user"] = user_info.get("username")
              print(session.get("user"))
              return "login OK!"
      
      if __name__ == '__main__':
          app.run("0.0.0.0",9527)

    5.2 Flask中的路由

    • @app.route()

      • 参数:

        • rule:路由地址,例,"/login"

        • methods:允许进入视图函数的请求方式

        • endpoint:mapping ,路由地址和endpoint之间的mapping,路由地址和视图函数之间还有一个mapping,注意,endpoint在同一个app中不能出现重复,默认值是视图函数名

        • defaults:默认路由参数

        • strict_slashes:是否严格遵循路由匹配规则

        • redirect_to:永久重定向 ,308和301

    from flask import Flask,request,url_for
    # url_for用于对endpoint设置的名称的反向获取url
    app = Flask(__name__)
    
    @app.route("/a",methods=["GET","PoSt","options","abc"],endpoint="end_homea" ,defaults={"count":20},strict_slashes=True,redirect_to="/")
    def homea(count):
        # print(url_for("end_homea"))  
        # 如果在蓝图里面,要使用url_for("蓝图标识".endpoint)
        # 如文件名为app01,则url_for("app01.end_homea")
        count = request.args.get("count",count)
        return f"200OK!{count}"
    示例
    • 动态参数路由

      • 可以分页,获取文件,解决分类,解决正则路由问题

      from flask import Flask,request,url_for
      # url_for用于对endpoint设置的名称的反向获取url
      app = Flask(__name__)
      
      @app.route("/get_img/<filename>")
      def get_img(filename):
          filepath = os.path.join("img", filename)
          return send_file(filepath)
      
      # 访问地址 : http://127.0.0.1:5000/info/1
      @app.route("/info/<int:nid>", methods=["GET", "POST"], endpoint="r_info")
      def student_info(nid):
          print(url_for("r_info",nid=2))  # /info/2,反向url时也要加上参数
          return f"Hello Old boy {nid}"   # Python3.6的新特性 f"{变量名}"

    5.3 Flask初始化配置

    • 常用配置:

      • template_folder:模板文件存放路径

      • static_folder:静态文件存放目录,默认值是static

      • static_url_path:静态文件访问路径,默认值是/ + static_folder

    from flask import Flask
    app = Flask(__name__,template_folder="templatess",
                static_folder="static",static_url_path="/futeng")
    # static == 你的家
    # /static == 回家路
    # /futeng == 富腾路
    示例

    5.4 Flask实例配置

    • Flask实例配置,即app的配置,可以在app.default_config中查看

      # 在setting.py中,写配置对应的类
      import hashlib,time
      class DebugConfig(object):
          DEBUG = True
          SECRET_KEY = "#$%^&*($#$%^&*%$#$%^&*^%$#$%"
          PERMANENT_SESSION_LIFETIME = 3600
          SESSION_COOKIE_NAME = "I am Not Session"
      
      class TestConfig(object):
          TESTING = True
          SECRET_KEY = hashlib.md5(f"{time.time()}#$%^&*($#$%^&*%$#$%^&*^%$#$%{time.time()}".encode("utf8")).hexdigest()
          PERMANENT_SESSION_LIFETIME = 360000
          SESSION_COOKIE_NAME = "#$%^&*($#$%^&*%$#$%^&*^%$#$%"
      
      # 在可执行文件中,调用
      from flask import Flask
      from setting import DebugConfig,TestConfig
      
      app = Flask(__name__)
      app.config.from_object(DebugConfig)
      # 或 app.config.from_object(TestConfig)

    5.5 Flask的蓝图

    • 蓝图:Blueprint,不能被run的Flask实例 不存在Config
      # 在可执行文件中,调用
      from flask import Flask
      from users import bp
      from car_users import car_bp
      
      app = Flask(__name__)
      app.register_blueprint(bp)
      app.register_blueprint(car_bp)
      from flask import Blueprint
      bp = Blueprint("app01",__name__)
      
      @bp.route("/add_user")
      def add_user():
          return "添加用户"
      
      @bp.route("/find_user")
      def find_user():
          return "查看用户"
      
      @bp.route("/drop_user")
      def drop_user():
          return "删除用户"
      
      @bp.route("/up_user")
      def up_user():
          return "修改用户"
      users.py
      from flask import Blueprint
      car_bp = Blueprint("app02",__name__,url_prefix="/car")
      # 参数url_prefix是为了区分users.py和car_users.py中的相同路由
      # 使用url_prefix后,访问时,要在原来路由前加上url_prefix设置的路径
      
      @car_bp.route("/add_user")
      def add_user():
          return "car添加用户"
      
      @car_bp.route("/find_user")
      def find_user():
          return "car查看用户"
      
      @car_bp.route("/drop_user")
      def drop_user():
          return "car删除用户"
      
      @car_bp.route("/up_user")
      def up_user():
          print("I am vf")
          return "car修改用户"
      car_users.py

    5.6 Flask特殊装饰器

    • @app.before_request:在请求进入视图函数之前 做出处理

    • @app.after_request:在响应返回客户端之前,结束视图函数之后

    @app.before_request
    def be1():
        print("I am Be1")
        # return "第一个请求你就过不去"  # 5种
    
    @app.before_request
    def be2():
        print("I am Be2")
        # return "第二个请求过不去了" # 5种
    
    @app.after_request
    def af1(res):
        print("I am af1")
        return res
    
    @app.after_request
    def af2(res):
        print("I am af2")
        return res
    示例
      • be+af 请求生命周期:

        • 正常:be1 -> be2 -> vf -> af2 -> af1

        • 异常:be1 -> af2 -> af1

        • 注意:只要有响应返回 , af全部执行

    • @app.errorhandler:重定义错误页面

    @app.errorhandler(404)        # 只能是 4xx,5xx
    def error404(error_message):
        print(error_message)
        # return redirect("https://www.autohome.com.cn/111232130/#pvareaid=33112794r5454") # 5种类型
    示例

    5.7 Flask的CBV

    • CBV:继承 MethodView 让当前的 class 可以成为视图类

      from flask import Flask
      app = Flask(__name__)
      
      class Login(views.MethodView): 
          def get(self,*args,**kwargs):
              return "I am Get"
      
          def post(self,*args,**kwargs):
              pass
      
      app.add_url_rule("/login",
                       endpoint="my_login",    # 可以不设置endpoint
                       view_func=Login.as_view(name="loginlogin")   # view_func.__name__="loginlogin"
                      )

    六、Flask组件:Flask-Session

    • 下载:pip install Flask-Session

    • 使用:

      from flask import Flask
      from flask_session import Session
      from redis import Redis
      
      app = Flask(__name__)
      app.config["SESSION_TYPE"] = "redis"
      app.config["SESSION_REDIS"] = Redis(host="192.168.12.87",port=6379,db=10)
      # app.session_interface # Flask 利用 session_interface 选择 Session 的存放位置 和 存放机制
      # app.default_config
      
      Session(app)    

    七、Flask总结

    1. 下载:pip install Flask

    2. 启动:

      from flask import Flask
      app = Flask(__name__)
      app.run("0.0.0.0",9527)        # 监听地址 监听端口
    3. response

      # Flask 三剑客
      return ""    # 返回字符串 HTTPResponse
      return render_template("index.html")    # 返回模板 依赖MarkupSafe 默认存放路径:templates 
      return redirect("/login")    # 响应头中加入 - location:重定向
      # Flask 特殊Response
      return send_file(文件名称或文件路径+文件名称)    # 打开并返回文件内容 自动识别文件类型 响应头 Content-Type:文件类型
      return jsonify({k:v})    # 返回标准格式的json字符串 响应头中 Content-Type:application/json
      # Flask 1.1.1中直接返回dict 本质上是在运行jsonify
    4. request

      from flask import request
      request.form  # 获取FormData中的数据 to_dict() 类似字典类型 .get() ["key"] KeyError
      request.args  # 获取URL中的数据 to_dict() 类似字典类型 .get() ["key"] KeyError
      request.json  # 请求头中 Content-Type:application/json 存放字典
      request.data  # 请求头中 Content-Type没有Form FormData 存放bytes类型
      
      request.method  # 请求方式
      request.cookies  # 浏览器的cookie键值对
      request.headers  # 请求头
      request.path  # 路由地址 请求地址
      request.url  # 请求全部地址
      request.host  # 主机位
    5. session 交由客户端保管机制 保管一串儿加密字符串

      from flask import session
      app.config["SECRET_KEY"] = "@#$%^&*(()*&^%$#*&^&%^&*)"
      session["key"] = "value"
    6. 路由配置 route

      @app.route(
          rule,    # 路由地址
          endpoint,    # mapping 名称对应路由地址 url_for 反向推算路由地址 url_for(endpoint)
          methods,    # 允许进入视图函数的 请求方式
          redirect_to,    # 永久重定向 301 308
          strict_slashes,    # 是否严格遵循路由匹配规则
          defalut,    # 默认参数
      )
      
      @app.add_url_rule(
          rule,
          view_func,    # 视图函数
      )
      
      # 动态参数路由
      @app.route("/get_file/<filename>")
      def get_file(filename):
          pass
    7. 初始化配置

      Flask(__name__,
            template_folder,    # 模板存放文件
            static_folder,    # 静态文件存放目录
            static_url_path,    # 静态文件访问路径
           )
    8. config

      class DebugConfig(object):
          DEBUG = True
          ...
      
      app.config.from_object(DebugConfig)
    9. 蓝图 Blueprint 当成不能被 run 的 Flask 实例

      from flask import Blueprint
      bp = Blueprint("bp",__name__,
                    url_perfix,    # url前缀
                    )
      @bp.route("/mybp",endpont="666")
      @bp.add_url_rule
      @bp.before_request
      @bp.after_request
      @bp.errorhandler(http错误码)
      
      url_for("蓝图标识".endpoint)
      url_for("bp.666")
      
      app.register_blueprint(bp)
    10. 特殊装饰器

      @app.before_request    # 在请求进入视图函数之前,做出处理
      @app.after_request    # 在结束视图函数之后,响应客户端之前
      @app.errorhandler(http错误码)    # 重定向错误信息
      def error404(errorMessage):
          pass
    11. CBV

      from flask import views
      class Login(views.MethodView):
          def get(self,*args,**kwargs):
              pass
      app.add_url_rule("/login",endpoint=None,view_func=Login.as_view(name="loginlogin"))
      # name是当前视图函数名,必须唯一,因为他是endpoint
  • 相关阅读:
    详解CUDA编程
    卷积神经网络详解
    python操作Excel读写--使用xlrd
    pycharm启动慢 –xms -xmx相关参数设置
    pycharm开发python利器入门
    win10安装windows live writer 错误:OnCatalogResult:0x80190194
    curl 模拟请求
    Python AES_ECB_PKCS5加密代码
    如何让eclipse恢复默认布局
    eclipse中项目jdk1.8刷新下就变成1.5的解决办法
  • 原文地址:https://www.cnblogs.com/zengyi1995/p/11322989.html
Copyright © 2020-2023  润新知