• Flask基础


    Flask基础

    前言

    • django:1个重武器,包含 了web开发中常用的功能,组件的框架,
    • Tornado:2大特性就是异步非阻塞、原生支持WebSocke协议
    • Flask:封装功能不及django完善、性能不及Tornado、但是拥有强大的第三方开源组件:http://flask.pocoo.org/extensions/
    • Flask是一个基于Python开发并且依赖jinja2模板和werkzeug WSGI服务的一个微型框架,对于Werkzeug本质是Socket服务端,其用于接收http请求并对请求进行预处理,然后触发Flask框架,开发人员基于Flask框架提供的功能对其请求进行相应的处理,并返回给用户,如果要返回给用户复杂的内容时,需要借助jinja2模板来实现对模板的处理,即:将模板和数据进行渲染,将渲染后的字符创返回给用户浏览器。

    安装

    为了不与现行项目想冲突,通常情况下会引入一个虚拟环境,将Flask安装在虚拟环境下,虚拟环境用virtualenv

    使用Virtualenv创建虚拟环境

    img

    将Flask安装在该虚拟环境里

    img

    或者不配置直接

    pip install flask
    
    #Flask依赖一个实现wsgi协议的模块:werkzeug
    from werkzeug.wrappers import Request, Response
    
    @Request.application
    def hello(request):
        return Response('Hello World!')
    
    if __name__ == '__main__':
        from werkzeug.serving import run_simple
        run_simple('localhost', 4000, hello)
    

    flask依赖于wsgi,实现wsgi模块,wsgiref,werkzeug,uwsgi

    简单使用

    最简使用

    from flask import Flask
    
    # 1 创建一个Flask实例
    app = Flask(__name__)
    
    
    @app.route('/')  # 路由系统生成,视图对应url
    def first_flask():  # 视图函数
        return 'Hello World'
    
    
    if __name__ == '__main__':
        app.run()  # 启用socket
    

    配置文件

    • 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  # 主模块所在的目录的绝对路径,默认项目目录
      
    • 更改配置文件

      app.config.from_object('settings.Base') # 更改配置文件
      
    • 通过路径,找到类并获取类中的静态字段

      FWQOF1.md.png

    • 配置文件

    FWlLng.png

    路由系统

    @app.route('url地址',methods=['POST',"GET"],endpoint='别名',defaults={'nid':1},strict_slashes=True,redirect_to='/index') 
    endpoint:反向生成url,默认是函数名 
    endpoint= 相当于django中的name 
    url_for 相当于 reverse 不定义默认是函数名
    defaults:默认参数
    strict_slashes:True 严格要求访问的方式,
    redirect_to='url':301重定向:永久重定向
    
    • 接收字符串类型

      from flask import Flask
      
      app=Flask(__name__)
      
      @app.route('/<name>')  #设置url传参数 
      def first_flask(name):  #视图必须有对应接收参数
          print(name)
          return 'Hello World'  #response
      
      
      if __name__ == '__main__':
          app.run()
      
    • 接收int,float,接收path链接类型

      # int类型
      @app.route('/<int:age>/')
      # float类型
      @app.route('/<float:salary>/') 
      # path类型
      @app.route('/<path:url>/')
      def first(url):   # 视图必须要对应接收参数   
          pass
      
    • 指定允许的请求方法

      @app.route('/<path:url>/',methods=['get']) #只允许get请求
      
    • 通过别名反向生成url

      from flask import Flask,url_for
      app=Flask(__name__)
      @app.route('/<path:url>',endpoint='name1')  #设置别名
      def first_flask(url):
          print(url_for('name1',url=url)) #如果设置了url参数,url_for(别名,加参数)
          return 'Hello World'
      
      if __name__ == '__main__':
          app.run()
      

    扩展:正则匹配url

    from flask import Flask, views, url_for
                from werkzeug.routing import BaseConverter
    
                app = Flask(import_name=__name__)
    
    
                class RegexConverter(BaseConverter):
                    """
                    自定义URL匹配正则表达式
                    """
                    def __init__(self, map, regex):
                        super(RegexConverter, self).__init__(map)
                        self.regex = regex
    
                    def to_python(self, value):
                        """
                        路由匹配时,匹配成功后传递给视图函数中参数的值
                        :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
    
                # 添加到flask中
                app.url_map.converters['regex'] = RegexConverter
    
    
                @app.route('/index/<regex("d+"):nid>')
                def index(nid):
                    print(url_for('index', nid='888'))
                    return 'Index'
    
    
                if __name__ == '__main__':
                    app.run()
    
    • 路由与反向路由

    视图

    • FBV

      from flask import Flask,render_template
      app = Flask(__name__)
      # 导入配置
      app.config.from_object('settings.类名')
      
      @app.route('/')
      def index():
      	return render_template('index.html')
      if __name__ == '__main__':
          app.run()
      
    • CBV (不常用)

      # 1.导入views
      from flask import views  
      # 写一个类,与django中类似
      class User(views.methodView):
      	methods = ['GET']
      	decorators = ['装饰器']
      	def dispatch_request(self,*args,**kwargs):
      		pass
          def get(self):
              self.dispatch_request()
              pass
          def post(self):
              pass
      # 加入app中
      app.add_url_rule('/user/', view_func=User.as_view(name='endpoint'))
      

      请求中request参数(常用)

      request.form   #存放form表单中的序列化数据
      request.args  # 存放url里面的序列化数据
      request.values.to_dict() # 存放url和from中的所有数据
      request.method # 存放请求方式
      request.path   # 路由地址
      request.url  所有的url:全部地址
      request.host 主机位 127.0.0.1:5000
      request.host_url 将主机位转换成url http://127.0.0.1:5000/
      request.data    查看传过来所有解析不了的内容
      request.json  查看前端传过来的json文件
      request.headers  查看所有的请求头
      file = request.files  前端传过来的文件
      file.filename 返回文件名称
      file.save()  :保存文件
      

      响应相关

      return '字符窜'
      return render_template() # 返回一个页面
      return redirect()  # 重定向
      return jsonify # 返回json数据,返回标准的json格式字符串 content-type: application/json
      

      模板渲染

      {{ }} 引用变量 非逻辑代码时使用
      {% %} 逻辑代码使用
      -基本数据类型,可以执行python语法,如dict.get(),list[11]
      -传入函数
          django,可以执行
          flask,不自动执行
      -全局定义
          @app.template_global()
          @app.template_filter()
      -定义模板
          {% extends %} 引入模板
          {% include %} 引入一小部分
      -宏定义
          {% macro ccc(name,type='text',value='') %}
             <input type="{{ type }}" name={{ name }} value={{ value }}>
              <input type="button" value="提交">
           {% endmacro %}
      
           {{ ccc('name') }} 运行
      
      -安全
         -前端 {{ll|safe}}
         -后端 Markup('字符串')
      

      session

      # 1导入session
      from flask import session
      # 2需要对其进行加盐,也可以在配置文件中对其加盐
      app.secret_key = 'alfkjs'
      # 3存session
      session['key'] = value
      # 4取值
      session.get('key', None)
      

    flash(闪现)

    • 闪现,在session中存储一个数据,读取时通过pop将数据移出,

      flash('内容', category= 'ok')
      # 第一个存的内容
      # category 是别名,相当于分lei
      get_flashed_messages()  # 取所有
      # 获取flash中的值,
      # get_flashed_messages()中的参数:
      with_categories=True  # True表示返回一个元祖
      category_filter='ok'  #取出别名为ok的所有参数,放在一个列表中
      # 如果想将列表发送到前段,需要导入jsonify
      
      
    • 应用场景

      假设在A页面做个操作,但该操作失败了,要跳转到B页面并显示这些错误信息

      from flask import Flask,flash,get_flashed_messages,request,redirect
      app = Flask(__name__)
      app.debug = True
      app.secret_key="1234"
      
      @app.route("/index")
      def index():
          # get请求用request.args.get, v是接受参数的变量
          # 浏览器请求:
          val = request.args.get('v')
          if val =="body":
              return "hello World, guys"
          # 如果请求参数不是body,则跳转到错误页面,需要将错误信息flash,也就是设置错误值到某个地方
          # A.flash不分类,直接设置值
          flash("前端输入参数错误")
          # B.flash还可以对错误信息,进行分类
          flash("前端输入参数错误", category="x1")
          return redirect("/error")
      
      @app.route("/error")
      def error():
          '''
          显示错误信息
          '''
          # 跳转到error页面后,请求时获取错误信息
          # A.flash没有分类时
          # data = get_flashed_messages()   # 获取的就是flash的值
          # B. 对于有分类的,取值时可以通过 category_filter 根据分类取错误信息
          data = get_flashed_messages(category_filter=['x1'])
          # 可能有很多的错误信息,也可以按照索引的方式取出错误值
          if data:
              msg = data[0]
          else:
              msg = "..."
          return "错误信息:%s" %(msg)
      
      if __name__=="__main__":
          app.run()from flask import Flask,flash,get_flashed_messages,request,redirect
      app = Flask(__name__)
      app.debug = True
      app.secret_key="1234"
      
      @app.route("/index")
      def index():
          # get请求用request.args.get, v是接受参数的变量
          # 浏览器请求:
          val = request.args.get('v')
          if val =="body":
              return "hello World, guys"
          # 如果请求参数不是body,则跳转到错误页面,需要将错误信息flash,也就是设置错误值到某个地方
          # A.flash不分类,直接设置值
          flash("前端输入参数错误")
          # B.flash还可以对错误信息,进行分类
          flash("前端输入参数错误", category="x1")
          return redirect("/error")
      
      @app.route("/error")
      def error():
          '''
          显示错误信息
          '''
          # 跳转到error页面后,请求时获取错误信息
          # A.flash没有分类时
          # data = get_flashed_messages()   # 获取的就是flash的值
          # B. 对于有分类的,取值时可以通过 category_filter 根据分类取错误信息
          data = get_flashed_messages(category_filter=['x1'])
          # 可能有很多的错误信息,也可以按照索引的方式取出错误值
          if data:
              msg = data[0]
          else:
              msg = "..."
          return "错误信息:%s" %(msg)
      
      if __name__=="__main__":
          app.run()
      

    中间件

    请求 执行前会执行一个wsgi_app,所以这里就是重写这个方法

    from flask import Flask
    app = Flask(__name__)
    
    @app.route("/login", methods=['GET', 'POST'])
    def index():
        pass
    
    class Md(object):
        def __init__(self, old_wsgi_app):
            self.old_wsgi_app = old_wsgi_app
    
        def __call__(self, environ, start_response):
            print("开始之前")
            ret = self.old_wsgi_app(environ, start_response)
            print("结束之后")
            return ret
    
    if __name__ =="__main__":
        app.wsgi_app = Md(app.wsgi_app) # 相当于把wsgi_app给更新了
        app.run()
    

    蓝图

    • 蓝图的作用,就是将功能和主服务分开.

    • 简单理解就是用蓝图实例化一个对象,然后由这个对象写一个函数,再加入flask中

    • 使用蓝图的目标:

      1. 构造程序目录

      2. 自定义程序目录

        批量处理url

        定制模板路径和静态文件路径

        请求扩展:

          - 可以针对app, 即全部程序都生效

          - 也可以针对单个的蓝图,即只有在执行该蓝图时,请求扩展才会生效

    简单实例:

    • 目录结构

      img

    • s_view.py中内容

      from flask import Blueprint  # 导入 Flask 中的蓝图 Blueprint 模块
      
      sv = Blueprint("sv", __name__)  # 实例化一个蓝图(Blueprint)对象
      
      
      @sv.route("/svlist")  # 这里添加路由和视图函数的时候与在Flask对象中添加是一样的
      def view_list():
          return "svlist_view_list"
      
    • manager.py中的内容

      from flask import Flask
      
      # 导入此前写好的蓝图模块
      from student_view import s_view
      
      app = Flask(__name__)  # type:Flask
      
      # 在Flask对象中注册蓝图模块中的蓝图对象 s_view 中的 sv
      app.register_blueprint(s_view.sv)
      
      app.run("0.0.0.0",5000)
      # 现在Flask对象中并没有写任何的路由和视图函数
      

      然后开启服务,就可以访问了http://127.0.0.1:5000/svlist

    • 详情:请查看博客: https://www.cnblogs.com/95lyj/p/9509229.html

    特殊的装饰器

    @app.template_global() 全局函数
    @app.template_filter() 类似标签
    
    @app.before_request     类似于process_request:没有参数 顺序:从上到下
    @app.after_request      类似于process_response:必须有参数,并返回 顺序:从下到上:即使在第一个就返回,也会全部执行一遍
    @app.before_first_request  只有第一次执行
    @app.teardown_request    在有异常发生时触发
    
    @app.errorhandler(404)  没找到页面走这个之下的函数,有一个参数
    

    @app.before_request 用来做认证模块

    @app.before_request
    def process_request(*args, **kwargs):
        # 验证表示,任何地址请求都会先执行before_request,所以登录验证就可以在before_request里做用户认证功能了
        print("其他请求之前就执行了process_request")
        # 4.访问/login的时候还没有登录,就会一直重定向到登录页,所以就要设置个白名单,如果请求地址是/login,就返回None
        if request.path == "/login":
            return None
        # 1.登录验证功能
        user = session.get('user_info')
        # 2.如果登录信息正常,什么都不做,程序继续其他执行
        if user:
            return None
        # 3.如果登录验证不通过,就重定向到登录页面
        return redirect("/login")
    

    @app.after_request after_first_request

    在请求进入视图函数之前执行,与他作用相同的@app.before_first_request也是,不同点是这个只执行一次,而after_request可以执行多次,按照顺序执行

    @app.after_request
    def foot_log(environ):
        if request.path != "/login":
            print("有客人访问了",request.path)
        return environ
    

    注意:

    但是要注意,多个请求扩展的执行顺序是有差别的:
    对于before_request,是按照写的代码的顺序从上到下的顺序正序执行的
    对于after_request, 是按照写的代码的顺序从下到上的顺序倒序执行的
    
    如果before_request return了(即程序被拦截了),其他before_request就不执行了,但是所有的after_request都会继续执行
    

    实例

    from flask import Flask,render_template,request,redirect,session,url_for
    app = Flask(__name__)
    app.debug = True
    app.secret_key = 'siuljskdjfs'
    
    @app.before_request
    def process_request1(*args,**kwargs):
        print('process_request1 进来了')
        return "拦截"
    
    @app.before_request
    def process_request2(*args,**kwargs):
        print('process_request2 进来了')
    
    @app.after_request
    def process_response1(response):
        print('process_response1 走了')
        # after_request 必须返回 response
        return response
    
    @app.after_request
    def process_response2(response):
        print('process_response2 走了')
        return response
    
    # 视图函数
    @app.route('/index',methods=['GET'])
    def index():
        print('index函数')
        return "Index"
    
    
    if __name__ == '__main__':
        app.run()
    

    执行结果:

    img

    errorhandler(404) 定制错误信息

    from flask import Flask,render_template,request,redirect,session,url_for
    app = Flask(__name__)
    app.debug = True
    app.secret_key = 'siuljskdjfs'
    
    # 经常会出现url不存在的情况,一般会有错误信息
    # 而这个错误信息也是可以进行定制的,根据错误码定制错误信息方法如下:
    @app.errorhandler(404)
    def error_404(arg):
        return "404错误了"
    
    # 视图函数
    @app.route('/index',methods=['GET'])
    def index():
        print('index函数')
        return "Index"
    
    if __name__ == '__main__':
        app.run()
    

    模板中定制方法(定制模板方法): template_global() 和 template_filter()

    from flask import Flask,request
    app = Flask(__name__)
    app.debug = True
    
    # 这就是基于请求扩展的 定制模板方法
    # 相对于在模板里定制了一个函数
    
    @app.template_global()
    def sb(a1, a2):
        return a1 + a2
    
    if __name__ == '__main__':
        app.run()
    
    
    #在HTML里调用的方式如下:
    {{sb(1,2)}}
    
    from flask import Flask,request
    app = Flask(__name__)
    app.debug = True
    
    # 这就是基于请求扩展的 定制模板方法
    @app.template_filter()
    def db(a1, a2, a3):
        return a1 + a2 + a3
    
    if __name__ == '__main__':
        app.run()
    
    #在HTML里调用的方式如下:
     {{ 1|db(2,3)}   # 参数 a1 = 1,是第一个参数; a2=2 是第二个参数;   a3=3 是第三个参数
    

    before_first_request

    • 应用场景:数据库的连接,初始化操作

      from flask import Flask,request
      app = Flask(__name__)
      app.debug = True
      
      # 内部其实就有个判断,初始值是FALSE,第一次执行后将值改变为True,以后判断后就不执行了
      @app.before_first_request
      def before_first_request2():
          print('before_first_request2')
      
      if __name__ == '__main__':
          app.run()
      
  • 相关阅读:
    C语言与汇编的嵌入式编程:统计字符串中各字符出现的次数
    一个汇编小demo
    C语言与汇编的嵌入式编程:求100以内素数
    TCP网络调试助手上提示错误:“1035 未知错误”的有效解决方法,本人实测确实可行
    一个支持国密SM2/SM3/SM4/SM9/ZUC/SSL的密码工具箱
    DRM(device resource management)介绍
    TODO
    pinctrl(1)——pinctrl子系统的使用
    GPIO使用总结
    Gerrit使用技巧
  • 原文地址:https://www.cnblogs.com/yuncong/p/10200325.html
Copyright © 2020-2023  润新知