码迷,mamicode.com
首页 > 其他好文 > 详细

Flask基础

时间:2019-01-01 12:33:00      阅读:233      评论:0      收藏:0      [点我收藏+]

标签:auth   字段   ica   als   The   server   eth   dfa   安全   

   开篇引入                                                    

1.django和flask的相同点和不同点?

共同点:都是基于wsgi的
不同点:
(1) django是一个大而全的框架,提供了方便内置的框架,orm,admin,分页,form,model_form,缓存,信号等很多方便的组件,
我们只要在配置文件中一修改就可以使用。
(2) flask是一个短小精悍,可扩展性非常强,flask适合开发小型的网站,也可以开发大型网站,因为它给我们提供有许多第三方组件,我们就可以结合这些第三方组件集成一个像django一样拥有很多功能的web框架。可定制性非常强。
(3) flask和django最大的不同点:request/session
flask是直接导入的,request在全局起作用。
django是依附于request参数,通过参数传导。
两个框架没有优劣之分,具体应用看实际需求。

2.什么是wsgi?

web服务网关接口,wsgi是一个协议,实现该写一个的模块:
- wsgiref
- werkzeug
实现协议的模块本质上就是socket服务端用于接收用户请求,并处理。
一般web框架基于wsgi实现,这样实现关注点分离,主要负责业务处理。
技术分享图片
 1 from wsgiref.simple_server import make_server
 2 
 3 def run_server(environ, start_response):
 4 start_response(200 OK, [(Content-Type, text/html)])
 5 return [bytes(<h1>Hello, web!</h1>, encoding=utf-8), ]
 6 
 7 
 8 if __name__ == __main__:
 9 httpd = make_server(127.0.0.1, 8000, run_server)
10 httpd.serve_forever()
wsgiref示例
技术分享图片
1 from werkzeug.wrappers import Response
2 from werkzeug.serving import run_simple
3 
4 def run_server(environ, start_response):
5     response = Response(hello)
6     return response(environ, start_response)
7 
8 if __name__ == __main__:
9     run_simple(127.0.0.1, 8000, run_server)
werkzeug示例
技术分享图片
from werkzeug.wrappers import Response
from werkzeug.serving import run_simple

class Flask(object):
    def __call__(self,environ, start_response):
        response = Response(hello)
        return response(environ, start_response)

    def run(self):
        run_simple(127.0.0.1, 8000, self)



app = Flask()

if __name__ == __main__:
    app.run()
flask源码入口

  基础知识                                                                     

安装

  pip3 install flask

特点: 短小精悍、可扩展强 的一个Web框架。

特色:上下文管理机制
wsgi:web service getway interface web服务网管接口
依赖wsgi:werkzurg

技术分享图片
from werkzeug.wrappers import Request, Response
from werkzeug.serving import run_simple

def run(environ,start_response):
    response = Response(‘hello‘)
    return response(environ, start_response)

if __name__ == ‘__main__‘:
    run_simple(‘localhost‘, 4000, run)
werkzurg示例一
技术分享图片
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)
werkzurg示例2

   一、配置文件                                                                              

1.使用:所有配置都在app.config中

  • 方式一:app.config[‘DEBUG‘] = True

    PS: 由于Config对象本质上是字典,所以还可以使用app.config.update(...)

  • 方式二:app.config.from_object(‘类的路径‘)
技术分享图片
class Config(object):
    DEBUG = False
    TESTING = False
    DATABASE_URI = sqlite://:memory:


class ProductionConfig(Config):
    DATABASE_URI = mysql://user@localhost/foo


class DevelopmentConfig(Config):
    DEBUG = True


class TestingConfig(Config):
    TESTING = True
settings.py

2.实现原理
  指定一个字符串(类的路径),importlib-->getattr 找到这个类把这个类的所有字段(大写)一个一个获取,获取的时候判断isupper(),给一个路径‘settings.Foo‘,可以找到类并获取其中大写的静态字段。

技术分享图片
import importlib

path = settings.Foo

p,c = path.rsplit(.,maxsplit=1)
m = importlib.import_module(p)
# m = __import__(p)
cls = getattr(m,c)


for key in dir(cls):
    if key.isupper():
        print(key,getattr(cls,key))
实现原理.py
技术分享图片
ENV:                                  None,
DEBUG:                                None,                        是否开启DEBUG模式
TESTING:                              False,                        是否开启测试模式
PROPAGATE_EXCEPTIONS:                 None,
PRESERVE_CONTEXT_ON_EXCEPTION:        None,
SECRET_KEY:                           None,
PERMANENT_SESSION_LIFETIME:           timedelta(days=31),
USE_X_SENDFILE:                       False,
SERVER_NAME:                          None,
APPLICATION_ROOT:                     /,
SESSION_COOKIE_NAME:                  session,
SESSION_COOKIE_DOMAIN:                None,
SESSION_COOKIE_PATH:                  None,
SESSION_COOKIE_HTTPONLY:              True,
SESSION_COOKIE_SECURE:                False,
SESSION_COOKIE_SAMESITE:              None,
SESSION_REFRESH_EACH_REQUEST:         True,
MAX_CONTENT_LENGTH:                   None,
SEND_FILE_MAX_AGE_DEFAULT:            timedelta(hours=12),
TRAP_BAD_REQUEST_ERRORS:              None,
TRAP_HTTP_EXCEPTIONS:                 False,
EXPLAIN_TEMPLATE_LOADING:             False,
PREFERRED_URL_SCHEME:                 http,
JSON_AS_ASCII:                        True,
JSON_SORT_KEYS:                       True,
JSONIFY_PRETTYPRINT_REGULAR:          False,
JSONIFY_MIMETYPE:                     application/json,
TEMPLATES_AUTO_RELOAD:                None,
MAX_COOKIE_SIZE: 4093,
})
默认配置文件

  二、路由系统                                                                    

重点:基于装饰器实现

技术点:-functools.wraps(func):保留原函数的原信息

装饰器(带参数)

  • methods=[‘GET‘,‘POST‘]
  • endpoint:反向生成url
  • url_for(endpoint)

自定义装饰器放下面
  注意事项:

  • - endpoint默认是函数名
  • - 不要让endpoint重名,如果重名函数也一定要相同。
  • - 加装饰器时要加functools.wraps(func) + functools.partial

路由设置的两种方式 

技术分享图片
方式一
    @app.route(/xxx)
        def index():
            return "index"

方式二
    def index():
        return "index"
    app.add_url_rule("/xxx",None,index)
路由设置的两种方式
技术分享图片
-动态路由
    /index/<int:nid>
    def index(nid):
        print(nid)
        return index
动态路由
技术分享图片
1 rule,                       URL规则
2 view_func,                  视图函数名称
3 endpoint=None,              名称,用于反向生成URL,即: url_for(名称)
4 methods=None,               允许的请求方式,如:["GET","POST"]
5 strict_slashes=None,        对URL最后的 / 符号是否严格要求,
6 redirect_to=None,           重定向到指定地址
7 
8 defaults=None,              默认值,当URL中无参数,函数需要参数时,使用defaults={k:v}为函数提供参数
9 subdomain=None,             子域名访问
参数
技术分享图片
from flask import Flask,url_for

app = Flask(__name__)

# 步骤一:定制类
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

# 步骤二:添加到转换器
app.url_map.converters[reg] = RegexConverter

"""
1.用户发送请求
2.flask内部进行正则匹配
3.调用to_python(正则匹配的结果)方法
4.to_python方法的返回值会交给视图的函数
"""

# 步骤三:使用自定义正则
@app.route(/index/<reg("\d+"):nid>)
def index(nid):
    print(nid,type(nid))
    print(url_for(index,nid=987))
    return index


if __name__ == __main__:
    app.run()
自定义正则

  三、视图                                                                     

 视图:FBV和CBV 

 技术点:反射

技术分享图片
视图函数中获取request或session
    方式一:直接找LocalStack获取
            from flask.globals import _request_ctx_stack
            print(_request_ctx_stack.top.request.method)
            
    方式二:通过代理LocalProxy(小东北)获取
            from flask import Flask,request
            print(request.method)
FBV
技术分享图片
import functools
from flask import Flask,views
app = Flask(__name__)


def wrapper(func):
    @functools.wraps(func)
    def inner(*args,**kwargs):
        return func(*args,**kwargs)

    return inner


class UserView(views.MethodView):
    methods = [GET]
    decorators = [wrapper,]

    def get(self,*args,**kwargs):
        return GET

    def post(self,*args,**kwargs):
        return POST

app.add_url_rule(/user,None,UserView.as_view(uuuu))

if __name__ == __main__:
    app.run()
CBV

  四、请求相关                                                              

  技术点:面向对象的封装

 

技术分享图片
    # 请求相关信息
        # 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
        # obj = request.files[‘the_file_name‘]
        # obj.save(‘/var/www/uploads/‘ + secure_filename(f.filename))
请求相关

   五、响应相关                                                                               

技术点:面向对象的封装

响应体:4种

技术分享图片
return 欢迎使用
return jsonify({k1:v1})
return render_template(xxx.html)
return redirect(/index)
4种响应体

定制响应头

技术分享图片
obj = make_response(render_template(index.html))
obj.headers[xxxxx] = 123
obj.set_cookie(key,value)
return obj
定制响应头

示例程序:用户访问限制

技术分享图片
@app.route(/detail/<int:nid>)
def detail(nid):
    if not session.get(user):
        return redirect(url_for(login))
    info = STUDENT_DICT[nid]
    return render_template(detail.html, info=info)
版本一
技术分享图片
def auth(func):
    @wraps(func)
    def inner(*args,**kwargs):
        if not session.get(user):
            return redirect(url_for(login))
        ret = func(*args,**kwargs)
        return ret
    return inner
        
@app.route(/index)
@auth
def index():
    return render_template(index.html,stu_dict=STUDENT_DICT)
    
应用场景:比较少的函数中需要添加额外的功能
版本二
技术分享图片
@app.before_request
def xxxx():
    if request.path == /login:
        return None
    if not session.get(user):
        return None
    return redirect(url_for(login))

应用场景:比较多的函数中需要添加额外的功能
版本三

  六、模板渲染                                                                               

-基本数据类型:可以执行python语法,如:dict.get()   list[‘xx‘]

传入函数

  • -django,自动执行
  • -flask,不自动执行

全局定义函数

技术分享图片
@app.template_global()
def sb(a,b):
    return a+b

@app.template_filter() # 适合if 判断
def db(a,b,c):
    return a+b+c
全局定义函数

模板继承

技术分享图片
layout.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        {% block content %}{% endblock %}
    </body>
    </html>
    
tpl.html
    {% extends layout.html %}
    {% block content %}
        {% include form.html %}

        {% macro ccccc(name,type=text, value=‘‘) %}
            <h1>宏</h1>
            <input type="{{ type }}" name="{{ name }}" value="{{ value }}">
            <input type="submit" name="提交">
        {% endmacro %}

        {{ ccccc(n1) }}
        {{ ccccc(n2) }}

    {% endblock %}
模板继承extends
技术分享图片
<include from.html>

form.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <div>sasasasa sasasasasasa</div>
    </body>
</html>
include

技术分享图片
{% macro ccccc(name,type=text, value=‘‘) %}
    <h1>宏</h1>
    <input type="{{ type }}" name="{{ name }}" value="{{ value }}">
    <input type="submit" name="提交">
{% endmacro %}

{{ ccccc(n1) }}
{{ ccccc(n2) }}

安全

   前端:{{ txt|safe }}
   后端:Markup(txt)

   七、session                                                                  

  原理:加密后放置在用户浏览器的cookie中

  流程:

技术分享图片
请求到来:flask读取cookie中的session对应的值:eyJrMiI6NDU2LCJ1c2VyIjoiZmVpIn0,将该值解密并反序列化成字典,放入内存以便视图函数使用。
视图函数:
    @app.route(/sess)
    def sess():
        print(session.__class__)
        session[k1] = 123
        session[k2] = 456
        del session[k1]
        return Session

请求结束,flask会读取内存中字典的值,进行序列化+加密,写入用户cookie中。
session执行流程

  实现原理(源码)

技术分享图片
def wsgi_app(self, environ, start_response):
            """
            1.获取environ并对其进行再次封装
            2.从environ中获取名称为session的cookie,解密,反序列化
            3.两个东西放到‘某个神奇‘的地方
            """
            # ctx = RequestContext(self, environ)  #self是app对象,evviron是原始数据对象
            # ctx.request = Request(environ)
            # ctx.session = None
            ctx = self.request_context(environ)
            error = None
            try:
                try:
                    ctx.push()
                    # 4. 执行视图函数
                    # 5.‘某个神奇’获取session,加密,序列化,写入cookie
                    response = self.full_dispatch_request()
                except Exception as e:
                    error = e
                    response = self.handle_exception(e)
                except:
                    error = sys.exc_info()[1]
                    raise
                return response(environ, start_response)
            finally:
                if self.should_ignore_error(error):
                    error = None
                """
6.‘某个神奇‘位置清空
"""
ctx.auto_pop(error)
源码

   闪现(flash): 在session存储一个数据,读取时通过pop将数据删除,形成一种数据只能取一次的效果

技术分享图片
from flask import Flask,flash,get_flashed_messages

@app.route(/page1)
def page1():
    flash(临时数据存储,error)
    flash(sasasasa,error)
    flash(sasasas,info)
    return Session

@app.route(/page2)
def page2():
    print(get_flashed_messages(category_filter=[error]))
    return Session
闪现示例

  八、中间件                                                                                

      一般不常用,因为它的执行顺序很靠前,无序获取request对象,与请求相关信息难以获得

  • call方法什么时候触发?

    -用户发起请求时,才执行

  • -任务:在执行call方法之前,做一个操作,call方法执行之后,做一个操作
技术分享图片
class Middleware(object):
    def __init__(self,old):
        self.old = old

    def __call__(self, *args, **kwargs):
        print()
        ret = self.old(*args,**kwargs)
        print()
return ret

if __name__ == __main__:
    app.wsgi_app = Middleware(app.wsgi_app)
    app.run()
中间件示例

  九、特殊的装饰器                                                                     

    技术点:before_request和after_request的实现原理

技术分享图片
before_first_request

before_request 视图函数之前,原理是将视图函数放入到一个列表中,循环,如果有返回值停止循环,后面的函数也将不会执行

after_request  视图函数之后,原理是将视图函数放入到一个列表中reverse,循环执行

template_global

template_filter

errorhandler
    @app.errorhandler(404)
    def not_found(arg):
        print(arg)
        return 404 没找到
6大装饰器

  十、蓝图                                                                                   

 (1) 目标:目录结构的划分
 (2) 自定义模板、静态文件

admin = Blueprint(
  admin,
  __name__,
  template_folder=templates,
  static_folder=static
)

(3)  给某一类url添加前缀变量 app.register_blueprint(admin, url_prefix=‘/admin‘)
(4)  给一类url添加before_request

@app.before_request
def x1():
    print(app.before_request)

  什么是响应式布局?                                                            
  技术点:@media  

技术分享图片
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.css" />
</head>
<body>
    <div class="row" style="background-color: #28a4c9">
      <div class="col-lg-6">.col-lg-6</div>
      <div class="col-lg-6">.col-lg-6</div>
    </div>
    <div class="row" style="background-color: #67b168">
        <div class="col-md-6">.col-md-6</div>
        <div class="col-md-6">.col-md-6</div>
    </div>
    <div class="row" style="background-color: red">
        <div class="col-sm-6">.col-sm-6</div>
        <div class="col-sm-6">.col-sm-6</div>
    </div>
    <div class="row" style="background-color: gold">
        <div class="col-xs-6">.col-xs-6</div>
        <div class="col-xs-6">.col-xs-6</div>
    </div>
</body>
</html>
响应式布局.html
技术分享图片
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <style>
        body{
            margin: 0;
        }
        .pg{
            width: 100%;
            background-color: rebeccapurple;
        }
        @media (min-width: 666px) {
            .pg{
                background-color: green;
            }
        }
        @media (min-width: 888px) {
            .pg{
                background-color: red;
            }
        }
    </style>
</head>
<body>
    <div>
        <div class="pg">asdfasdf</div>
    </div>
</body>
</html>
本质

 

注:

技术分享图片
1. 上下文管理的实现?
    当请求到来的时候,
        flask会把请求相关和session相关信息封装到一个ctx = RequestContext对象中,
        会把app和g封装到app_ctx = APPContext对象中去,
        通过localstack对象将ctx、app_ctx对象封装到local对象中

    获取数据(执行视图函数的时候)
        通过导入一个模块,用模块.的方式获取我们想要的数据
        实现细节:
            通过localproxy对象+偏函数,调用localstack去local中获取对应的响应ctx、app_ctx中封装值
            问题:为什么要把ctx = request/session  app_ctx = app/g
                因为离线脚本需要使用app_ctx
    请求结束
        调用localstk的pop方法,将ctx和app_ctx移除    
2. 为什么把上下文管理分成:
    - 应用上下文:request/session
    - 请求上下文: app/g 
    离线脚本应用
            
3. Local的作用?
    类似于threading.local的作用,但是是他的升级版(greentlet.get_current())
    __storage__ = {
        1231: {},
        1231: {}
    }
4. LocalStack作用?
    将Local中__storage__的值维护成一下结构:
    __storage__ = {
        1231: {stack:[],},
        1231: {stack:[],}
    }
5. 为什么要维护成一个栈?

6. 为什么导入request,就可以使用?
    每次执行request.xx方法时,会触发LocalProxy对象的__getattr__等方法,由方法每次动态的使用
    LocalStack去Local中获取数据
flask进阶问题预览

 

Flask基础

标签:auth   字段   ica   als   The   server   eth   dfa   安全   

原文地址:https://www.cnblogs.com/zhangyafei/p/10204463.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!