干货来啦。Flask框架看这一篇就够了,关注不迷路,Jeff带你看源码。开发技术时时更新

一、初识Flask

1.1 什么是flask?

img

Flask 本是作者 Armin Ronacher在2010年4月1日的一个愚人节玩笑 ,不过后来大受欢迎,进而成为一个正式的python编写的web框架

Flask是一个Python编写的Web 微框架,让我们可以使用Python语言快速实现一个网站或Web服务,在介绍Flask之前首先来聊下它和Django的联系以及区别,django个大而全的web框架,它内置许多模块,flask是一个小而精的轻量级框架,Django功能大而全,Flask只包含基本的配置, Django的一站式解决的思路,能让开发者不用在开发之前就在选择应用的基础设施上花费大量时间。Django有模板,表单,路由,基本的数据库管理等等内建功能。与之相反,Flask只是一个内核,默认依赖于2个外部库: Jinja2 模板引擎和 WSGI工具集--Werkzeug , flask的使用特点是基本所有的工具使用都依赖于导入的形式去扩展,flask只保留了web开发的核心功能。

img

WSGI(web服务器网关接口)是python中用来规定web服务器如何与python Web服务器如何与Python Web程序进行沟通的标准,本质上就是一个socket服务端。而 Werkzeug模块 就是WSGI一个具体的实现

关键词:一个Python编写微web框架 一个核心两个库( Jinja2 模板引擎 和 WSGI工具集)

1.2 为什么要有flask?

flask性能上基本满足一般web开发的需求, 并且灵活性以及可扩展性上要优于其他web框架, 对各种数据库的契合度都非常高

关键词:1. 性能基本满足需求 2 .灵活性可拓展性强 3. 对各种数据库的契合度都比较高。

4.在真实的生产环境下,小项目开发快,大项目设计灵活

二、Flask快速启动

'''
pip install flask

'''
# 1 导入flask,我们要用flask,就必须导入Flask
from  flask import Flask
# 2 生成一个Flask对象,__name__表示当前文件的名字
app = Flask(__name__)



# 3 添加路由,flask用的是装饰器的模式
#注册路由,并写响应函数index
@app.route("/")
def index():
    return "Hello flask"

if __name__ == '__main__':
    #4 启动flask
    #run里面是执行了run_simple(host,port,self=app,也就是flask对象)
    app.run()

三、Flask四剑客

'''
响应字符串
响应html页面
跳转页面
返回json字符串
'''

from flask import Flask, render_template, redirect, jsonify

app = Flask(__name__)


@app.route("/index")
def index():
    # 1.返回字符串
    # return "hello word  啊啊啊"

    # 2.返回html页面
    # 返回html,从flask里面导入render_template
    # return render_template("index.html")

    # 3.跳转路由
    # return redirect('/login')

    # 4.返回数据转json返回,从flask中导入jsonify
    # data = {"name": "jeff", "age": "18"}
    # return jsonify(data)
    pass


@app.route("/login")
def login():
    return "我是login页面"


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

三、flask的配置文件

'''
四种配置flask方法配置
1.直接给app对象赋值属性
2.以字典的形式,给flask配置文件做配置
3.以文件的形式给flask做配置(django就是这种)
4.以类的形式,给flask做配置(推荐使用)
'''


from flask import Flask

app = Flask(__name__)

# 方式1(不推荐),因为只能配置两项。debug和secret_key
# app.debug = True  # 默认false,自动重启

# 方式2 字典的形式
# app.config["DEBUG"] = True


# 方式3 以文件的形式,在from_pyfile里传递路径
# app.config.from_pyfile("settings.py")


# 方式4 以类的形式,一个文件多个套配置,减少测试与更改(推荐使用)
# app.config.from_object("setobj.settings")

@app.route("/")
def index():
    return "json 是炮王"


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

方式三的配置文件:

类似Django一样,一个专门的配置文件

DEBUG = True

方式四的配置类:

优点:一个文件,多套配置。不同的类不同的配置,减少了测试与上线的更改的配置项

class settings():
    DEBUG = True

可以配置的属性

flask中的配置文件是一个flask.config.Config对象(继承字典),默认配置为:
'''
 default_config = ImmutableDict(
        {
            "ENV": None,
            "DEBUG": None,
            "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,
        }
    )
'''

四、flask路由

4.1 源码分析

# 源码分析:
'''
self.add_url_rule(rule, endpoint, f, **options)


def add_url_rule(
        self,  # app对象
        rule,  # url路由
        endpoint=None,  # 路由别名
        view_func=None,  # 响应的函数名
        provide_automatic_options=None,
        **options
    ):

methods :['POST','GET'] —》控制请求方法,不传默认只能GET方法
'''

# @app.route的本质就在执行add_url_rule
        # rule是路由,endpoint是路由别名,view_func是响应函数
        # 如果endpoint不传就是响应的函数名

4.2 使用1:起别名

from flask import Flask , url_for, redirect

app = Flask(__name__)


def index():
    print()
    return "我是index"

# @app.route('/detail/<int:nid>',methods=['GET','POST'],endpoint='detail')  # 典型写法
@app.route('/login')
def login():
    print(url_for("index1"))   # 走的别名,如果别名没有写,默认函数名
    return redirect(url_for("index1")) # 走的别名,如果别名没有写,默认函数名

# url_for:
    # 用endpoint获取路由要用url_for 在flask中导入,也就是反向解析

# 路由
app.add_url_rule('/', endpoint='index1', view_func=index)

# endpoint 别名
# view_func 响应的函数名


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

4.3 使用2:有名分组

from flask import Flask , url_for, redirect

app = Flask(__name__)

# 必须接收分组名字一样的  nid
def index(nid):
    print(nid)
    return "我是index"

# 路由:有名分组
app.add_url_rule('/index/<string:nid>', endpoint='index1', view_func=index, methods=['POST','GET'])

app.add_url_rule('/index/<int:nid>', endpoint='index', view_func=index, methods=['POST','GET'])

# 浏览器:http://127.0.0.1:5000/index/asdfas
# string、int 规定接收的类型


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

4.4 路由小总结

 总结:
 1 @app.route("/login") 的本质app.add_url_rule("/login",view_func=login),所以我们就可以用这两个方式来添加路由
 2 路由的参数,
    2.1 endpoint,做是反向解析,如果上面添加路由的时候,没有传递endpoint就是使用响应函数的函数名,反向解析用url_for(),做解析,这个url_for必须在flask里面导入
    2.2 methods=["POST","GET"],该参数控制路由允许哪些请求方法访问,如果不传,默认只能GET方法
    2.3 路由以及路由路由转化器。"/index/<int:nid>",<参数的类型:用哪个变量来接收>,响应函数中的形参的名字必须转化器中一致。

4.5 默认转化器

DEFAULT_CONVERTERS = {
    'default':          UnicodeConverter,
    'string':           UnicodeConverter,
    'any':              AnyConverter,
    'path':             PathConverter,
    'int':              IntegerConverter,
    'float':            FloatConverter,
    'uuid':             UUIDConverter,
}

4.6 自定义转换器

#1 写类,继承BaseConverter
#2 注册:app.url_map.converters['regex'] = RegexConverter
# 3 使用:@app.route('/index/<regex("\d+"):nid>')  正则表达式会当作第二个参数传递到类中
from flask import Flask, views, url_for
from werkzeug.routing import BaseConverter

app = Flask(import_name=__name__)
# 1.写类
class RegexConverter(BaseConverter):
    """
    自定义URL匹配正则表达式
    """
    def __init__(self, map, regex):
        super(RegexConverter, self).__init__(map)
        self.regex = regex

    def to_python(self, value):
        """
        路由匹配时,匹配成功后传递给视图函数中参数的值
        """
        return int(value)

    def to_url(self, value):
        """
        使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数
        """
        val = super(RegexConverter, self).to_url(value)
        return val
# 2.注册:添加到flask中
app.url_map.converters['regex'] = RegexConverter
# 正则匹配处理结果,要交给to_python,to_python函数可以对匹配处理结果做处理
# 3.使用:
@app.route('/index/<regex("\d+"):nid>')
def index(nid):
    print(url_for('index', nid='888'))
    return 'Index'

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

4.7 自定义转换器总结

1 导入from werkzeug.routing import BaseConverter
2 我写个继承BaseConverter。实现3个方法,def __init__ , def to_python , def to_url
3 将上面的类注册到app.url_map.converters['regex'] = RegexConverter中
4 然后就可以在路由转化器中使用3中的regex("传正则")
5 当路由被访问以后。regex("传正则")会匹配结果,把结果传递to_python,我们可以进行再次处理,to_python处理好的结果,会传递给响应函数的形参
6 当用url做反向解析的时候,传递给路由转化器的参数,会经过to_url,进行处理。处理以后,在拼接到路由。

五、flask模板渲染

py文件:

from flask import Flask,render_template,Markup
app = Flask(__name__)
app.debug = True


USERS = {
    1:{'name':'张三','age':18,'gender':'男','text':"道路千万条"},
    2:{'name':'李四','age':28,'gender':'男','text':"安全第一条"},
    3:{'name':'王五','age':18,'gender':'女','text':"行车不规范"},
}

def func1(arg,tank):
    return Markup(f"<h1>饼哥正帅,{arg} is sb {tank} is same as {arg}</h1>")

@app.route("/")
def index():
    # data = {
    #     "user" :USERS,
    #     "name": "jason"
    # }
    return render_template("index.html",user = USERS,name="jason",ht1 = func1,ht="<h1>饼哥正帅</h1>")
    #return render_template("index.html",**data)




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

1.变量的循环

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1> 我是html</h1>
<table>
{% for k,v in user.items() %}
   <tr>
       <td>{{ k }}</td>
       <td>{{ v.name }}</td>
       <td>{{ v['name'] }}</td>
       <td>{{ v.get('name') }}</td>
       <td>{{url_for("index")}}</td>
   </tr>
{% endfor %}
</body>
</html>

2.逻辑判断

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>用户列表</h1>
    <table>
        {% if name %}
          <h1>Hello {{ name }}!</h1>
        {% else %}
          <h1>Hello World!</h1>
        {% endif %}
    </table>
</body>
</html>

3.执行函数,传参数

比django中多可以加括号,执行函数,传参数

from flask import Flask,render_template,Markup
app = Flask(__name__)
app.debug = True



def func1(arg,tank):
    return Markup(f"<h1>饼哥正帅,{arg} is sb {tank} is same as {arg}</h1>")

@app.route("/")
def index():
    # data = {
    #     "user" :USERS,
    #     "name": "jason"
    # }
    return render_template("index.html",ht1 = func1,ht="<h1>饼哥正帅</h1>")



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

html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

{{ ht|safe}}
{{ht1("jaosn","tank")}}  // 传参数
</body>
</html>

六、flask的请求与响应

from flask import Flask, request, make_response

app = Flask(__name__)
app.debug = True


@app.route('/', methods=['POST', 'GET'])
def index():
    # print('请求方法',request.method)  # 请求方法
    # print('get请求的参数',request.args)  # get 请求的参数
    # print('post请求的参数',request.form)  # post请求的参数
    # print('请求的cookies',request.cookies)  # 请求的cookies
    # print('post与get的所有参数',request.values)  # post与get的所有参数

    # 响应头,添加make_response
    response = make_response('ok')
    #response = make_response(render_template("login.html"))
    # 设置cookie
    response.set_cookie("key","val")
    return 'OK'


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

请求相关的信息

 请求相关的信息
    print("请求方法",request.method)#请求方法
    print("get请求的参数",request.args)# get请求的参数
    print("post请求的参数",request.form) #post请求的参数
    print("post,与get的所有参数",request.values)#post,与get的所有参数
    print("请求的cookies",request.cookies)#请求的cookies
    请求相关信息
    request.method  提交的方法
    request.args  get请求提及的数据
    request.form   post请求提交的数据
    request.values  post和get提交的数据总和
    request.cookies  客户端所带的cookie
    request.headers  请求头
    request.path     不带域名,请求路径
    request.full_path  不带域名,带参数的请求路径
    request.script_root
    request.url           带域名带参数的请求路径
    request.base_url      带域名请求路径
    request.url_root      域名
    request.host_url      域名
    request.host          127.0.0.1:500

七、设置cookies

from flask import Flask, make_response

app = Flask(__name__)
app.debug = True


@app.route('/', methods=['POST', 'GET'])
def index():
    # 响应头,添加make_response
    response = make_response('ok')
    
    # 设置cookies
    response.set_cookie('key', 'val')  
    
    # 删除cookies
    response.delete_cookie("key")
    
    # 设置响应头
    response.headers["x-somexx"] = "A SB"
    
    return response


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

设置cookie的参数

key, 键
value='', 值
max_age=None, 超时时间 cookie需要延续的时间(以秒为单位)如果参数是\ None`` ,这个cookie会延续到浏览器关闭为止

expires=None, 超时时间(IE requires expires, so set it if hasn't been already.)
                   
path='/', Cookie生效的路径,/ 表示根路径,特殊的:根路径的cookie可以被任何url的页面访问,浏览器只会把cookie回传给带有该路径的页面,这样可以避免将cookie传给站点中的其他的应用。
                   
domain=None, Cookie生效的域名 你可用这个参数来构造一个跨站cookie。如, domain=".example.com"所构造的cookie对下面这些站点都是可读的:www.example.com 、 www2.example.com 和an.other.sub.domain.example.com 。如果该参数设置为 None ,cookie只能由设置它的站点读取
                   
secure=False, 浏览器将通过HTTPS来回传cookie
                   
httponly=False 只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)

八、flask的session

cookie:存放在客户端的键值对
session:存放在客户端的键值对
token:存放在客户端,通过算法来校验

8.1 设置session(使用版)

在使用session之前必须现在设置一下密钥

app.secret_key="asdas" # 值随便
# app.config['SECRET_KEY'] = os.urandom(24) # 配置session使用的秘钥
设置:session['username'] = 'xxx'
# 在django中发什么三件事,1,生成一个随机的字符串 2 往数据库存 3 写入cookie返回浏览器
# 在flask中他没有数据库,但session是怎样实现的?
    # 生成一个密钥写入这个cookie,然后下次请求的时候,通过这个cookie解密,然后赋值给session
    # 我们通过app.session_interface来查看
    
删除:session.pop('username', None)

Flask提供了session对象用来将cookie加密储存,session通过秘钥对数据进行签名以加密数据。

from flask import Flask, session
import os

app = Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(24) # 配置session使用的秘钥

@app.route('/')
def set_session_info():
    session['username'] = 'mark' # 使用用户信息配置sesion信息作为cookie,并添加到响应体中
    
    return '设置session信息'

session对象像可以字典一样操作,内部是把字典的信息进行加密操作然后添加到相应体中作为cookie,响应的时候会自动返回给浏览器。

 session['username'] = 'mark'
 session['userphone'] = '123456'  # 可以指定多条session信息,统一放到响应的cookie中返回给浏览器

8.2 设置session(分析版)

from flask import Flask,session

app = Flask(__name__)
# 要用session,必须app配置一个密钥
app.secret_key  =  "asdasdihasdiuh"

# 设置session的名字,默认配置文件中为:session
app.config['SESSION_COOKIE_NAME']="python13session" 

# app.session_interface

#app.session_interface实现了两个方法,一个叫save_session,一个open_session,
# save_session 存session执行,加密设置cookies
# open_session 取session执行,解密大字典,拿到session
@app.route("/",)
def index():
    #如何设置sessoion
    # 1 导入session
    # 2 给sessoion设置值
    session['name'] = "egon"
    return "ok"

@app.route("/login")
def login():
    print(session["name"])
    return "login"

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

8.3 设置session有效期

后端Flask跟浏览器交互默认情况下,session cookie会在用户关闭浏览器时清除。通过将session.permanent属性设为True可以将session的有效期延长为31天,也可以通过操作app的配置PERMANENT_SESSION_LIFETIME来设置session过期时间。

案例 3.3.2.1:开启指定session过期时间模式

from flask import Flask, session
import os

app = Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(24)


@app.route('/')
def set_session_info():
    session['username'] = 'mark'
    session['userphone'] = '123456'
    session.permanent = True # 开启设置有效期,默认为31天后过期
    return 'Hello World!'
...

设置自定义过期时间

# 通过设置PERMANENT_SESSION_LIFETIME指定具体的过期时间

from datetime import timedelta
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(hours=1) # 设置为1小时候过期

8.4 获取sessoin

在Flask中获取设置的session信息通过session对象获取,session对象是继承了字典类,所以获取的时候是字典的取值方式。其内部会把浏览器传过来的session信息解密。

@app.route('/get_session/')
def get_session():
    username = session.get('username')
    userphone = session.get('userphone')
    if username or userphone:
        return "{},{}".format(username, userphone)
    return "session为空"

1550930691065

8.5 删除session

session对象调用pop()可以根据具体的session的key清除掉指定的session信息。

session对象调用clear()可以清除此次请求的浏览器关于本域名的所有session信息

@app.route('/del_session/')
def del_session():
    session.pop('username')
    # session.clear()
    return '删除成功'

8.6 源码分析

session源码的执行流程

-save_seesion
    -响应的时候,把session中的值加密序列化放大到了cookie中,返回到浏览器中
-open_session
    -请求来了,从cookie中取出值,反解,生成session对象,以后再视图函数中直接用sessoin就可以了。

源码分析

# app.session_interface  点进去

class SecureCookieSessionInterface(SessionInterface):
   
    salt = "cookie-session"
   
    digest_method = staticmethod(hashlib.sha1)
  
    key_derivation = "hmac"
   
    serializer = session_json_serializer
    session_class = SecureCookieSession

    def get_signing_serializer(self, app):
        if not app.secret_key:
            return None
        signer_kwargs = dict(
            key_derivation=self.key_derivation, digest_method=self.digest_method
        )
        return URLSafeTimedSerializer(
            app.secret_key,
            salt=self.salt,
            serializer=self.serializer,
            signer_kwargs=signer_kwargs,
        )
    # 取session的时候执行的
    def open_session(self, app, request):
        s = self.get_signing_serializer(app)
        if s is None:
            return None
        ##cookie键是SESSION_COOKIE_NAME"=session
        val = request.cookies.get(app.session_cookie_name)

        print("open_session.session_cookie_name,", app.session_cookie_name, )
        if not val:
            return self.session_class()
        max_age = total_seconds(app.permanent_session_lifetime)
        try:
            data = s.loads(val, max_age=max_age)
            print("self.session_class(data)", self.session_class(data) )
            return self.session_class(data)
        except BadSignature:
            return self.session_class()

    #存session的时候执行的
    def save_session(self, app, session, response):
        domain = self.get_cookie_domain(app)
        path = self.get_cookie_path(app)

        # If the session is modified to be empty, remove the cookie.
        # If the session is empty, return without setting the cookie.
        if not session:
            if session.modified:
                response.delete_cookie(
                    app.session_cookie_name, domain=domain, path=path
                )

            return
        # Add a "Vary: Cookie" header if the session was accessed at all.
        if session.accessed:
            response.vary.add("Cookie")

        if not self.should_set_cookie(app, session):
            return
        httponly = self.get_cookie_httponly(app)
        secure = self.get_cookie_secure(app)
        samesite = self.get_cookie_samesite(app)
        expires = self.get_expiration_time(app, session)
        # 把session做了一个加密,把整个session的key--》val,全部加密,的到一个value值,
        #session是一个大字典,
        val = self.get_signing_serializer(app).dumps(dict(session))
        # 他把session加密后得到的val存到cookie里面了
        #cookie键是SESSION_COOKIE_NAME"=session
        print("源码中的session",dict(session))
        print("app.session_cookie_name,",app.session_cookie_name,)
        response.set_cookie(
            app.session_cookie_name,
            val,
            expires=expires,
            httponly=httponly,
            domain=domain,
            path=path,
            secure=secure,
            samesite=samesite,
        )

九、闪现

9.1 什么是闪现?

1554289488442

-设置:flash('aaa')
-取值:get_flashed_message()
-
-假设在a页面操作出错,跳转到b页面,在b页面显示a页面的错误信息
from flask import Flask,flash,get_flashed_messages

app  = Flask(__name__)
#app.session_interface
app.secret_key ="sdasd"
# 什么闪现:就像session一样,也是一个页面设置,另一个页面使用,我不管你在哪个页面调用的
# 只要调用一次,就清空了,
# 闪现的作用,一般用信息处理。假设用户,a页面做操作,产生了信息。我希望在b页面内获取。
# 但是我不知道用户在什么时候,访问b页面,但是只要用户一旦访问页面就把信息显示出来。
# 同一页面,同次请求是可以拿多次的
@app.route("/")
def index():
    #产生信息,message设置消息的,category给消息分类,如果不传默认用”message“

    flash("你错过了我")
    
    flash(message="你再次错过我",category="渣男")
    return "index"

@app.route("/login")
def login():
    
    #(with_categories=True,消息是否要带上分类信息,category_filter=["渣男"]对消息进行过滤,取指定的分类消息
    print(get_flashed_messages(with_categories=True,category_filter=["渣男"]))
    print(get_flashed_messages())
    return "login"


@app.route("/test")
def test():
    print(get_flashed_messages())
    return "test"

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

猜你喜欢

转载自www.cnblogs.com/guyouyin123/p/12521976.html
今日推荐