zoukankan      html  css  js  c++  java
  • flask入门

    Flask介绍

    Flask是一个使用 Python 编写的轻量级 Web 应用框架。其 WSGI 工具箱采用 Werkzeug(Django使用的是wsgiref) ,模板引擎则使用 Jinja2 。

    Flask特点:

    • 短小精悍,可拓展强,第三方组件丰富

    与Django的比较:

    • 大而全,内部提供:ORM、Admin、中间件、Form、ModelForm、Session、缓存、信号、CSRF;

    tornado:

    • 短小精悍+异步非阻塞

    简单的Werkzeug应用

    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', 4000, hello)
    

    Flask版Hello Word

    from flask import Flask
    
    app = Flask(__name__) # 一个Flask类的对象
    
    @app.route('/index') # 此乃路由也
    def index():
        return 'Hello World'
    
    if __name__ == '__main__':
        app.run() 

    路由系统

    路由与函数绑定的两种方式

    装饰器方式:

    from flask import Flask
    app = Flask(__name__)
    
    @app.route('/index',methods=['GET','POST'],endpoint='n1')
    def index():
        return "Index"
    
    # 参数介绍 rule=匹配路由,methods=允许请求方式,endpoint=反向解析的名字
    

    直接绑定

    from flask import Flask
    app = Flask(__name__)
    
    def order():
        return 'Order'
    
    app.add_url_rule('/order',view_func=order)
    # route内部就是用了这种方式
    

    动态路由匹配

    from flask import Flask
    app = Flask(__name__)
    
    @app.route('/index/<int:nid>',methods=['GET','POST'])
    def index(nid):
        return "Index"
    
    # <类型:关键字>
    # 类型转换器介绍
    DEFAULT_CONVERTERS = {
        'default':          UnicodeConverter,
        'string':           UnicodeConverter,
        'any':              AnyConverter,
        'path':             PathConverter,
        'int':              IntegerConverter,
        'float':            FloatConverter,
        'uuid':             UUIDConverter,
    }
    

    反向解析

    from flask import Flask,url_for
    app = Flask(__name__)
    
    @app.route('/index',methods=['GET','POST'],endpoint='n1')
    def index():
        v1 = url_for('n1') # 关键字传参
        print(v1)
        return "Index"
    

      解析的默认名字是函数名,当为视图函数使用装饰器时一定要修复,否则Flask项目会因存在因装饰器装饰多个视图函数,产生相同的endpoint而无法启动

    自定义正则匹配

    from flask import Flask, views, url_for
    from werkzeug.routing import BaseConverter
    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 = Flask(import_name=__name__)
    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()
    

    路由重定向

    from flask import Flask,render_template,redirect
    app = Flask(__name__)
    
    # 当访问此路由时会定位到/new
    @app.route('/index',methods=['GET','POST'],redirect_to='/new')
    def index():
        return "老功能"
    
    @app.route('/new',methods=['GET','POST'])
    def new():
        return '新功能'
    
    
    if __name__ == '__main__':
        app.run()
    

    子域名获取

    from flask import Flask,render_template,redirect
    app = Flask(__name__)
    app.config['SERVER_NAME'] = 'oldboy.com:5000'
    
    
    @app.route("/dynamic", subdomain="<username>")
    def xxxxxxx(username):
        print(username)
        return 'xxxxx'
    
    if __name__ == '__main__':
        app.run()

    路由的一些参数

    @app.route和app.add_url_rule参数:
    rule,                       URL规则
    view_func,                  视图函数名称
    defaults=None,              默认值,当URL中无参数,函数需要参数时,使用defaults={'k':'v'}为函数提供参数
    endpoint=None,              名称,用于反向生成URL,即: url_for('名称')
    methods=None,               允许的请求方式,如:["GET","POST"]
    strict_slashes=None,        对URL最后的 / 符号是否严格要求,
    subdomain=None,             子域名访问

    视图

    FBV的两种形式

    # 方式一
    from flask import Flask,render_template,redirect,views
    app = Flask(__name__)
    class IndexView(views.View):
        methods = ['GET']  # 允许方法
        decorators = [wapper, ] # 装饰器
    
        def dispatch_request(self):
            print('Index')
            return 'Index!'
    
    app.add_url_rule('/index', view_func=IndexView.as_view(name='index1'))  # name=endpoint
    
    
    # 方式二
    class IndexView(views.MethodView):
        methods = ['GET']
        decorators = [wapper, ]
    
        def get(self):
            return 'Index.GET'
    
        def post(self):
            return 'Index.POST'
    
    app.add_url_rule('/index', view_func=IndexView.as_view(name='index2'))  # name=endpoint

    请求和响应

    # 请求数据获取
    from flask import request
    # request.method
    # request.args
    # request.form
    # request.values
    # request.cookies
    # request.headers
    # request.path
    # request.full_path
    # request.script_root
    # request.url
    # request.base_url
    # request.url_root
    # request.host_url
    # request.host
    # request.files
    # request.get_data() 请求元数据 # obj = request.files['the_file_name'] # obj.save('/var/www/uploads/' + secure_filename(f.filename)) # 返回响应 # return "字符串" from flask import render_template # return render_template('html模板路径',**{}) from flask import redirect # return redirect('/index.html') 重定向 # 设置响应头和cookie from flask import make_response # response = make_response(render_template('index.html')) # response是flask.wrappers.Response类型 # response.delete_cookie('key') # response.set_cookie('key', 'value') # response.headers['X-Something'] = 'A value' # return response

    有坑

    读取文件form表单应设置enctype="multipart/form-data"

    模板

    Markup等价django的mark_safe

    Session

    除请求对象之外,还有一个 session 对象。它允许你在不同请求间存储特定用户的信息。它是在 Cookies 的基础上实现的,并且对 Cookies 进行密钥签名要使用会话,你需要设置一个密钥。

    • 设置:session['username'] = 'xxx'

    • 删除:session.pop('username', None)
    from flask import Flask,render_template,request,redirect,session
    
    app = Flask(__name__) # 一个Flask类的对象
    app.secret_key = 'u2jksidjflsduwerjl'  # 秘钥
    app.debug = True
    
    @app.route('/login',methods=['GET',"POST"])
    def login():
        if request.method == 'GET':
            return render_template('login.html')
        user = request.form.get('user') # 获取POST传过来的值
        pwd = request.form.get('pwd') # 获取POST传过来的值
        if user == 'alex' and pwd == '123':
            # 用户信息放入session
            session['user_info'] = user
            return redirect('/index')
        else:
            return render_template('login.html',msg ='用户名或密码错误')
            # return render_template('login.html',**{'msg':'用户名或密码错误'})
    
    
    @app.route('/logout')
    def logout():
        session.pop('user_info')
        return redirect('/login')

    配置文件

    flask中的配置文件是一个flask.config.Config对象(继承字典),默认配置为:

    {
    'DEBUG':                                get_debug_flag(default=False),  是否开启Debug模式
    'TESTING':                              False,                          是否开启测试模式
    'PROPAGATE_EXCEPTIONS':                 None,                          
    'PRESERVE_CONTEXT_ON_EXCEPTION':        None,
    'SECRET_KEY':                           None,
    '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,
        }
    

    设置方式

    # 方式一:
    app.config['DEBUG'] = True
    PS: 由于Config对象本质上是字典,所以还可以使用app.config.update(...)
    # 方式二:
    app.config.from_object("python类或类的路径")
    # 这样写类,
    class BaseConfig(object):
        DEBUG = True
        SECRET_KEY = "asudflkjdfadjfakdf"
    

      

    特殊装饰器

    from flask import Flask, Request, render_template
    
    app = Flask(__name__, template_folder='templates')
    app.debug = True
    
    # 只有第一次请求会执行
    @app.before_first_request
    def before_first_request1():
        print('before_first_request1')
    
    # 视图函数之前
    @app.before_request
    def before_request1():
        Request.nnn = 123
        print('before_request1')
    
    # 视图函数之后
    @app.after_request
    def after_request1(response):
        print('before_request1', response)
        return response
    
    # 自定义错误信息
    @app.errorhandler(404)
    def page_not_found(error):
        return 'This page does not exist', 404
    
    # 全局过滤函数
    @app.template_global()
    def sb(a1, a2):
        return a1 + a2
    
    # 可以向模板上下文中自动注入变量或函数(必须是字典),在模板中调用
    @app.context_processor 
    def inject_user():
          return dict(user=g.user)
    
    # 模板的过滤函数 {{a1|db(a2,a3)}}
    @app.template_filter()
    def db(a1, a2, a3):
        return a1 + a2 + a3
    
    
    @app.route('/')
    def hello_world():
        return render_template('hello.html')
    
    
    if __name__ == '__main__':
        app.run()

      before_request的执行顺序是按代码顺序执行的,after_request的执行数顺序是反向执行的,当before_request返回内容的时候,after_request会全部执行,之前版本的Django也是这样做的

    闪现威慑,点燃警告

    message是一个基于Session实现的用于保存数据的集合,其特点是:使用一次就删除。

    from flask import Flask,session,flash,get_flashed_messages
    app = Flask(__name__)
    app.secret_key = 'asdfasdfasdf'
    
    @app.route('/x1',methods=['GET','POST'])
    def login():
        flash('存一个',category='x1') # category是作为关键字,可以不设置
        return "视图函数x1"
    
    @app.route('/x2',methods=['GET','POST'])
    def index():
        data = get_flashed_messages(category_filter=['x1']) # category_filter根据关键字取,可以不设置
        return "视图函数x2"
    
    if __name__ == '__main__':
        app.run()

    中间件

    falsk中间件利用了装饰器的性质,他执行的时request,session还没有生成

    from flask import Flask
    app = Flask(__name__)
    app.secret_key = 'asdfasdfasdf'
    
    @app.route('/x2',methods=['GET','POST'])
    def index():
        return "x2"
    
    
    class Middleware(object):
        def __init__(self,old_wsgi_app):
            """
            服务端启动时,自动执行
            :param old_wsgi_app:
            """
            self.old_wsgi_app =old_wsgi_app
    
        def __call__(self, environ, start_response):
            """
            每次有用户请求道来时
            :param args:
            :param kwargs:
            :return:
            """
            print('before')
            from flask import session,request
            obj = self.old_wsgi_app(environ, start_response)
            print('after')
            return obj
    
    if __name__ == '__main__':
        app.wsgi_app = Middleware(app.wsgi_app)
        app.run()
    

    蓝图

    Flask 用 蓝图(blueprints) 的概念来在一个应用中或跨应用制作应用组件和支持通用的模式。

    # 简单的蓝图项目结构
    | - projectName  
        | - app  //程序包  
            | - templates //jinjia2模板  
            |- static //css,js 图片等静态文件  
            | - main  //py程序包 ,可以有多个这种包,每个对应不同的功能  
                | - __init__.py  
                |- errors.py  
                |- forms.py  
                |- views.py  
            |- __init__.py  
            |- email.py //邮件处理程序  
            |- models.py //数据库模型  
        |- requirements.txt //列出了所有依赖包以及版本号,方便在其他位置生成相同的虚拟环境以及依赖  
        |- config.py //全局配置文件,配置全局变量  
        |- manage.py //启动程序
    

    在app中的py文件中创建一个蓝图

    from flask import Blueprint
    
    us = Blueprint('us',__name__)
    
    @us.route('/info')
    def info():
        return 'info'
    

    注册蓝图

    创建flask的唯一实例,并且在初始化的时候注册蓝图

    在app中的init完成创建实例与注册蓝图的过程

    from flask import Flask
    from .app import views
    
    app = Flask(__name__)
    app.register_blueprint(views.us)
    

    项目的启动文件

    from app import app
    if __name__ == '__main__':
        app.run()
    

    蓝图的几个作用

    1. 为一个蓝图下的路由设置before_request和after_request
    2. 蓝图URL前缀:xxx = Blueprint('account', __name__,url_prefix='/xxx')
    3. 蓝图子域名:xxx = Blueprint('account', __name__,subdomain='admin')

    # 前提需要给配置SERVER_NAME: app.config['SERVER_NAME'] = 'wupeiqi.com:5000'
    # 访问时:admin.wupeiqi.com:5000/login.html

    信号

    Flask框架中的信号基于blinker,其主要就是让开发者可是在flask请求过程中定制一些用户行为。

    pip3 install blinker
    

    内置信号及执行顺序

    flask.signals.py
    appcontext_pushed = _signals.signal('appcontext-pushed') #  请求上下文push时执行
    request_started = _signals.signal('request-started') #  请求到来前执行
     
    如果有render:
    	before_render_template = _signals.signal('before-render-template') # 模板渲染前执行
    	template_rendered = _signals.signal('template-rendered') # 模板渲染后执行
    
    request_finished = _signals.signal('request-finished') # 请求结束后执行
    如果视图函数有异常:
    	got_request_exception = _signals.signal('got-request-exception') # 请求执行出现异常时执行
    
    request_tearing_down = _signals.signal('request-tearing-down') # 请求执行完毕后自动执行(无论成功与否)
    appcontext_tearing_down = _signals.signal('appcontext-tearing-down') # 请求上下文执行完毕后自动执行(无论成功与否)
    
    appcontext_popped = _signals.signal('appcontext-popped') # 请求上下文pop时执行
    
    
    如果使用信号:
    	message_flashed = _signals.signal('message-flashed') # 调用flask在其中添加数据时,自动触发
    

    信号的使用:

    from flask import signals
    def func(*args,**kwargs):
        pass
    #信号中注册函数
    signals.xxx.connect(func)
    

    自定义信号

    from flask import Flask, flash
    from flask.signals import _signals
    
    app = Flask(__name__)
    
    xinhao = _signals.signal("xinhao")  # 创建信号
    
    
    # 定义函数
    def func1(*args, **kwargs):
        print("func1", args, kwargs)
    
    
    def func2(*args, **kwargs):
        print("func2", args, kwargs)
    
    
    # 将函数注册到信号中,添加到这个列表
    xinhao.connect(func1)
    xinhao.connect(func2)
    
    
    @app.route("/zzz")
    def zzz():
        xinhao.send(sender='xxx', a1=123, a2=456)  # 触发这个信号,执行注册到列表中的所有函数,这里的参数个上面函数的参数一致
        return "发送信号成功"
    
    
    if __name__ == '__main__':
        app.run(debug=True)
    
    # 打印结果
    # func1 (None,) {'sender': 'xxx', 'a1': 123, 'a2': 456}
    # func2 (None,) {'sender': 'xxx', 'a1': 123, 'a2': 456}

    问题:信号和before_request区别?

    • before_request,可以控制请求是否可以继续往后执行。
    • 信号,在原来的基础增加额外的操作和值。

    多app应用

    from flask import Flask
    from werkzeug.wsgi import DispatcherMiddleware
    from werkzeug.serving import run_simple
    
    app01 = Flask('app01')
    app02 = Flask('app02')
    
    @app01.route('/login')
    def login():
        return 'app01.login'
    
    @app02.route('/index')
    def index():
        return 'app02.index'
    
    
    dm = DispatcherMiddleware(app01,{
    				'/app02':        app02,
    			})
    
    if __name__ == '__main__':
    	run_simple('localhost', 5000,dm)
    

    源码:

    class DispatcherMiddleware(object):
    
        """Allows one to mount middlewares or applications in a WSGI application.
        This is useful if you want to combine multiple WSGI applications::
    
            app = DispatcherMiddleware(app, {
                '/app2':        app2,
                '/app3':        app3
            })
        """
    
        def __init__(self, app, mounts=None):
            self.app = app
            self.mounts = mounts or {}
        # 请求来临时__call__方法被执行
        def __call__(self, environ, start_response):
            script = environ.get('PATH_INFO', '')
            path_info = ''
            while '/' in script:
                if script in self.mounts:  # 如果路由存在与self.mounts中,就说明是其他app的
                    app = self.mounts[script]
                    break
                script, last_item = script.rsplit('/', 1) # 倒序分割
                path_info = '/%s%s' % (last_item, path_info)# 将分割出去的在拼接起来
            else:
                app = self.mounts.get(script, self.app)
            original_script_name = environ.get('SCRIPT_NAME', '')
            environ['SCRIPT_NAME'] = original_script_name + script
            environ['PATH_INFO'] = path_info
            return app(environ, start_response) # app()执行
    

    离线脚本编写

    falsk中数据是在请求来临时通过RequestContext存在Local对象中,但是如果不是通过请求的方式我们要怎样获取数据

    with falskobj.app_context():
        pass
    

    源码:

    class Flask(_PackageBoundObject):
        def app_context(self):
            return AppContext(self)
    
    with 语法会去执行后面语句返回对象的__enter__方法,with语句结束后会执行__exit__方法
    class AppContext(object):
        def __enter__(self):
            self.push()
            return self
    
        def __exit__(self, exc_type, exc_value, tb):
            self.pop(exc_value)
    
            if BROKEN_PYPY_CTXMGR_EXIT and exc_type is not None:
                reraise(exc_type, exc_value, tb)
    

    这也是为什么使用LocalStack对Local对象进行操作?

    目的是想要将local中的值维护成一个栈,例如:在多app应用中编写离线脚本时,可以实用上。
    from m_app import app01,app02
    from flask import current_app
    """
    {
    	1231: {
    		stack: [app01,app02,]
    		}
    }
    """
    
    with app01.app_context():
        print(current_app)
         with app02.app_context():
              print(current_app)
        print(current_app)
    

      

  • 相关阅读:
    Light oj 1197
    UVA 11426 GCD
    Light oj 1236
    Light oj 1138
    Light oj 1214-Large Division (同余定理)
    Light oj 1234
    HDU
    ZOJ 3469 Food Delivery(* 区间DP 总结)
    二分查找整理
    zoj 3965 Binary Tree Restoring(* dfs)
  • 原文地址:https://www.cnblogs.com/wwg945/p/8921229.html
Copyright © 2011-2022 走看看