zoukankan      html  css  js  c++  java
  • 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()
      
  • 相关阅读:
    165. Compare Version Numbers
    164. Maximum Gap
    3、桶排序
    162. Find Peak Element
    160. Intersection of Two Linked Lists
    155. Min Stack
    154. Find Minimum in Rotated Sorted Array II
    153. Find Minimum in Rotated Sorted Array
    Linux/Unix系统编程手册 第二章:基本概念
    Linux/Unix系统编程手册 第一章:历史和标准
  • 原文地址:https://www.cnblogs.com/yuncong/p/10200325.html
Copyright © 2011-2022 走看看