flask:静态文件处理,localsetting配置,路由配置,CBV模式,模板,特殊装饰器

1.根据flask源码反推werkaueg的服务器的返回值

根据以下代码,找到返回值
from werkzeug.serving import run_simple
def func(environ, start_response):
    print('请求来了')
    # 填充的代码
    return ....
if __name__ == '__main__':
    run_simple('127.0.0.1', 11111, func)

分析

复制代码
前提:
    在flask里面,程序启动的时候会执行app.__call__方法__call__里面执行的run_simple是里面第三个参数加()来执行
    所以单独了解werkzeug的运行流程时,可以对应flask的启动方法来进行查找zwerlzeug服务器的启动流程
    
    # 第一步
    # 执行app.run()
    # 程序启动,等待用户请求到来,就执行run_simple里面的第三个参数,其中self就是app()
    # 也就是执行app.__call__方法,其中__call__返回什么,用户 看到的就是什么。

    # 第二步:
    # 其中__call__方法返回的是self.wsgi_app(environ, start_response),也就是说self.wsgi_app(environ, start_response)返回什么,用户看到的就是什么
    # 所以我们在单独使用 werkzeug服务器的时候,相当于里面的func函数,我们只需要将func函数的返回值返回给用户就ok了

    # 第三步:
    # 其中self.wsgi_app()函数返回的是response(environ, start_response),在该函数
    # 内部我们可以看到response 等于的response = self.full_dispatch_request()的返回值
    # 所以我们此时就是要将response=self.full_dispatch_request()返回值找到,就是用户的显示内容

    #第四步
    # 点进去self.full_dispatch_request方法,可以看到返回的是self.finalize_request(rv)
    # 其中full_dispatch_request里面的rv等于的就是我们第一步里面的函数的返回值,就是一个字符串,所以rv是什么,给用户返回的就是什么

    # 第五步:我们来查看rv是什么
    # 点击去self.finalize_request(rv)方法,我们可以看到response = self.make_response(rv),应该是将rv做了什么操作
    # 然后赋值给了response

    # 第六步:我们查看返回的response是什么东西
    # 点击去make_response函数,我们可以看到,在执行一些过滤rv的条件
    # 唯一符合标准的就是if not isinstance(rv, self.response_class),可以看到rv = self.response_class(rv, status=status, headers=headers)
    # 那么response_class到底是什么?
    # 我们可以看到response_class是Response类,从from flask.wrappers import Response导入的。将鼠标放到该文件上,能看到该文件的路径,然后导入
    # 那么rv是什么那?rv==self.response_class(rv, status=status, headers=headers)==Response(rv),rv是通过Response类进行一系列封装之后返回符合http协议的response响应
    # 所以我们可以继承这个类,写在werkzeug服务器里面就能返回了
    """
    # from werkzeug.serving import run_simple
    # from flask.wrappers import Response
    # def func(environ, start_response):
    #     print('请求来了')
    #     # 填充的代码
    #     response = Response('你好')  # 其中你好就等于rv ==Response(rv)
    #     # return response(environ, start_response)
    #     return response(environ, start_response)
    # if __name__ == '__main__':
    #     run_simple('127.0.0.1', 11111, func)
    """

    # 第七步
    # 我们在Response类里面可以看到继承了ResponseBase, JSONMixin两个类。
    # 其中ResponseBase是继承werkzeug服务器里面的BaseResponse类,所以等看到flask是基于werkzeug来作为wgsi服务器
    # from werkzeug.wrappers.base_response import BaseResponse
    # 所以我们werkzeug服务器的代码要修改一下
    """
    # from werkzeug.serving import run_simple
    # from werkzeug.wrappers.base_response import BaseResponse
    # def func(environ, start_response):
    #     print('请求来了')
    #     # 填充的代码
    #     response = BaseResponse('你好')
    #     # return response(environ, start_response)
    #     return response(environ, start_response)
    # if __name__ == '__main__':
    #     run_simple('127.0.0.1', 11111, func)
    """
    
    所以我们可以知道:werkzeug可以在不依赖flask的情况下,自己独立的作为一个web服务器来运行
    所以我们可以知道flask是基于werkzeug服务器来充当web服务器的
复制代码

2.静态文件

复制代码
app = Flask(__name__)
一般都是用__name__,使用其他的字符串也可以,但是一般是不更改,因为有一个root_path和instance_path有关
root_path: 默认情况下,root_path通过Flask()实例化传入的__name__来计算程序的根目录
    
其中static_url_path和static_folder参数是和静态文件相关的
template_folder="设置模板路径"

static_folder="static":表示静态文件的目录是static
static_url_path="/yy":不写,默认就是static,但是和static_folder的static是不一样的。指的是想引入静态文件的时候,通过什么别名来找到文件
前端显示:<img src="/kobe/1.png" alt="">
前端显示:<img src="{{ url_for('static',filename='1.png')}}"> 推荐使用
     其中static指的是静态文件的目录 区别是: 使用第二种的时候,在static_url_path在发生变化的时候会自动的变化。个人认为这个参数是为了隐藏静态文件的路径。也可能是为了以后静态文件的迁移会方便一点
复制代码

3.配置文件

pymysql信息写到配置文件 # https://blog.csdn.net/brightgreat/article/details/124636690
配置文件是通过app.config.from_object('config.setting')来引入的
基于全局变量的配置文件
复制代码

  settings:是服务器上的配置文件
  localsettings:是本地开发是的配置文件
  其中flask的加载的时候会自动的将配置文件加载进去,在setting里面将localsettings导入进去,就能在视图函数
  中去读取配置文件的DB_HOST等配置。
  这样写的好处是:
    在以后我们上传代码时,可以不上传localsettings我们本地的数据库配置等文件,让运维人员通过我们文件的格式
    在发布的时候,写在setting文件里面。这样视图函数也能获取到配置,并且有利于安全,不会好多人都知道密码

 在使用git的时候,.ingnore文件来过滤掉不上传的文件,就可以将localsetting文件过滤掉
 格式如下: config/localsettings.py 就过滤掉,不上传到缓存区!

或者也可以这样理解:
  1.在settings.py里面写上我们本地开发的数据库账号密码,也导入localsettings.py文件
  2,在代码上传的时候,也传入localsetting文件。在服务器上部署的时候,
  3.在localsetting文件里面写上真实的数据库账号密码。这样也能保证安全
复制代码

  基于类的方式来引入配置文件

复制代码

 app.config.from_object('config.test1.DevTestings') # DevTestings是test1里面的类名

 缺点:
  得手动去改配置文件,上面基于去基于全局变量的就需要运维人员去改就可以了

复制代码

关于配置文件

django的配置文件的方式只有基于全局变量这一种方式,也就是localsettings
flask可以鸡鱼类,也可以基于配置文件的方式

 4.路由系统

复制代码
源码分析:
    app = Flask(__name__)
    1.在创建app对象的时候,将很多有用的信息都封装到对象里面了。可以点击去Flask()里面去查看
    
    @app.route('/index')
    def index():
        return "index"
    2.在执行这段的时候,会将路由(url)和视图函数创建对应关系,存放到app的一个对象(map对象里面)里面。如果有多个url和视图的对应关系,
      会将这多个对应关系都封装到app的对象里面。
      注意:
        2.1 在执行带参数的装饰器的时候,先执行的是@app.route('/index'),会返回一个函数,比如是f,
            然后再将index传到f里面,f返回的是什么,index的返回值就是什么
        2.2 所以先执行route()函数,其返回了一个decorator的函数名
        2.3 在继续执行的时候,将index传递到decorator里面,相当于是一个闭包,其实也等价于
            @decorator
            def index():
                return "index"
        2.4 在执行decorator的时候,会去取值endpoint,如果没有,就默认为None。
            也会执行self.add_url_rule(rule, endpoint, f, **options)
            其中rule是url,endpoint就是函数的endpoint,f就是被装饰的函数index
        2.5 add_url_rule()方法首先会判断endpoint是否为空,为空的话,就将被装饰的函数名赋值给endpoint
        2.6 add_url_rule里面会去执行一个rule = self.url_rule_class(rule, methods=methods, **options)类,创建了一个rule的对象,
            其中里面的rule就是传递过来的url也就是index,methods就是能被url执行的请求方式的列表
            url_rule_class()这一步做的就是将url和methods打包做成了一个rule对象
        2.7 然后执行self.url_map.add(rule)将rule对象加入到map对象里面,说白了map对象有好多的ruel的规则,rule的里面有好多url和视图的对应关系
        2.8 所以add_url_rule方法做的就是将url和methods封装成rule对选哪个,将rule对象放到map对象里面,map对象在app对象的url_map里面
    3. 所以是decorator内部的add_url_rule()方法是将url和视图的对应关系放到flask里面的具体操作
    4.所以是视图函数也可以这样写,也可以添加路由关系
        from flask  import Flask,request,render_template
        app = Flask(__name__)

        @app.before_request
        def f1():
            print('befor')

        # @app.route('/index')
        def index():
            print('123')
            return "456"
        app.add_url_rule('/index','index',index)
        #add_url_rule(rule, endpoint, f, **options)
        # 第一个/index是url,第二个是endpoint,第三个是函数名
        if __name__ == '__main__':
            app.run(
                host='127.0.0.1',
                port='12122'
            )
    5.所以路由的加载流程是:
        5.1 将url和methods封装成rule对象
        5.2 将rule对象封装到map对象中
        5.3 其中mao对象在app.url_map里面
        5.4 app.url_map的内容如下:
            Map([<Rule '/index' (GET, HEAD, OPTIONS) -> index>,
                <Rule '/index' (GET, HEAD, OPTIONS) -> index>,
                <Rule '/static/<filename>' (GET, HEAD, OPTIONS) -> static>]
            )    
动态路由:
    @app.route('/login')
    def login():
        return render_template('login.html')
        
    @app.route('/login/<name>')
    def login(name):
        print(type(name))
        return render_template('login.html')
        
    @app.route('/login/<int:name>')
    def login(name):
        print(type(name))
        return render_template('login.html')
        
支持正则表达式的路由
    from flask import Flask,render_template

    app = Flask(__name__)


    from werkzeug.routing import BaseConverter
    class RegConverter(BaseConverter):
        def __init__(self, map, regex):
            super().__init__(map)
            self.regex = regex
    app.url_map.converters['regex'] = RegConverter

    @app.route('/index/<regex("\d+"):x1>')
    def index(x1):
        return render_template('index.html')

    if __name__ == '__main__':
        app.run()
复制代码

5.视图函数
  FBV模式

复制代码
第一种写法
    from flask import Flask
    app = Flask(__name__)
    app.config.from_object('config.test1.DevTestings')
    @app.route('/index')
    def index():
        return "hell world!"
    if __name__ == '__main__':
        app.run(
          host='127.0.0.1',
          port= 10089,
        )
        
第二种写法
    from flask import Flask,request,render_template
    app = Flask(__name__)

    @app.before_request
    def f1():
        print('befor')

    # @app.route('/index')
    def index():
        print('123')
        return "456"
    app.add_url_rule('/index','index',index) # 推荐
    #add_url_rule(rule, endpoint, f, **options)
    # 第一个/index是url,第二个是endpoint,第三个是函数名
    if __name__ == '__main__':
        app.run(
        host='127.0.0.1',
        port='12122'
        )
复制代码

  CBV模式

复制代码
from flask import Flask,views,request
app = Flask(__name__)

def test(func):
    def inner(*args,**kwargs):
        print('befor')
        result = func(*args,**kwargs)
        print('after')
        return result
    return inner

class UserView(views.MethodView):
    methods = ['GET','POST'] # 能过滤,添加上说明只支持get方法
    decorators = [test,]  # 相当于是装饰器,可以加多个装饰器,cbv加装饰器的方式
    def get(self):
        print('get')
        return "get"

    def post(self):
        print('post')
        print(request.data)
        username = request.form.get('username')
        passwd = request.form.get('password')
        print(username,passwd)
        return "post"

# CBV模式添加路由只能使用这样的方式,MethodView里面的eg
# app.add_url_rule('/counter', view_func=CounterAPI.as_view('counter'))
# 其中counter代表url,CounterAPI代表试图的类,counter表示endpoint
app.add_url_rule('/user', view_func=UserView.as_view('user'))
# /user也可以添加参数 /user/<int:nid>,但是get方法得接受一下def get(self,nid):
if __name__ == '__main__':
    app.run(
        host="127.0.0.1",
        port='54321'
    )
# 其原理与django的cbv模式是一样的,都是通过反射来实现的
复制代码

6.模板

flask的模板可以传递字典,列表等,与django的模板方法类似
还可以传递函数名,在前端加括号执行
注意:如果想给所有的模板提供一个公共的方法
  要在函数钱加一个装饰器@app.template_global()

  

视图函数

复制代码
from flask  import Flask,request,render_template
app = Flask(__name__)

def testa(name):
    return  "dsb" + name
@app.route(
'/la') def la(): user = [11,22,33] return render_template('la.html',user=user,f=testa) pass if __name__ == '__main__': app.run( host='127.0.0.1', port='12321' )
复制代码

la.html

复制代码
{% extends 'lyout.html'%}

{% block content %}
    {{user[0]}}
    {% for i in user %}
    <input type="text" value="{{i}}">
    {% endfor %}
{{f('科比')}}
{% include 'head.html' %}
{% endblock %}
复制代码

head.html

复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<h1>this is include</h1>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
</body>
</html>
复制代码

layout.html

复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<h1></h1>
{% block content%}

{% endblock %}
<h1></h1>

<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
</body>
</html>
复制代码

给所有的视图返回页面添加方(定义全局的模板方法)

复制代码
from flask  import Flask,request,render_template
app = Flask(__name__)

@app.template_global()  # 全部模板都可以使用该函数,是全局的 前端调用方式{{ testa('科比')}}
def testa(name):   # 前端调用的也是相同的函数名
    return  "dsb" + name

@app.template_filter() # 也是全局的, 前端调用方式{{'科比'|testb('18')’}}
def testb(name,age):
    return  "dsb" + name +age

@app.route('/la')
def la():
    return render_template('la.html')

if __name__ == '__main__':
    app.run(
        host='127.0.0.1',
        port='12322'
    )

总结:
  1.相当于django的tag标签
  2.因为是全局的,所以是全部视图函数都可以使用,但是在蓝图里面使用的话,就在本蓝图有效,应用范围不同
复制代码

7.特殊的装饰器

注意:
  类似与django的中间件,可以这样理解

视图函数

复制代码
from flask  import Flask,request,render_template
app = Flask(__name__)

@app.before_request  # 一般用于用户登录认证,before_request也可以放在下面
def f1():
   if request.path == '/login':
    return # 返回空也是可以的,会继续向下走
print('befor') @app.after_request def f2(ss): print('after') return ss # 这里面的ss=response对象,是返回给用户的 @app.route('/index') def index(): print('123') return "123"

注意:如果after装饰的函数不返回值的话,会报错,f2() takes 0 positional arguments but 1 was given
因为:
  按照django的中间件来看process_response里面的返回值也是给用户看的
  所以before不写参数,没有返回值,因为一旦有返回值,就不会向后走了,就直接将before的返回值返回给用户了
  但是after装饰的函数必须有返回值,因为是返回给用户看的
源码解析:
  其实在flask为这两个'中间件'维护了两个列表
  当flask程序从上向下执行的时候,会依次将被before装饰的函数放到列表中,当视图函数开始执行时,从before列表中从第0个元素的函数开始执行,直到最后一个,然后执行视图函数
  当视图函数执行完之后,要执行after列表,也依次将读取到的程序加载到after列表中,只不过在执行after列表的时候,先将after列表resever(翻转),在执行列表里面的函数
复制代码

before_request的其他调用(这样就是主动地调用before_request)

复制代码
from flask  import Flask,request,render_template
app = Flask(__name__)

@app.route('/index')
def index():
    print('123')
    return "123"

# @app.before_request # 如果主动执行的话,函数上就不写装饰器了
def f1():
    print('befor')

app.before_request(f1)
# xq = app.before_request(index)  # 也可以将其赋值回来,但是也是函数的地址,可以点进去看
# print(xq)
# print(index)
总结:
  其实不管怎么使用before_request,其本质是一样的,都是将函数传递都before_request里面去执行,只不过这种方式是主动的执行
复制代码

总结:

before_request和after_request在蓝图里面也是可以使用的。
但是其作用域也只是在本蓝图内有效,可以将before_requets和after_request定义到蓝图里面。

其他知识点:

threading.local()方法:给每一个线程开辟自己的空间,来存储自己的数据。
注意:
  虽然flask没有threading.local(),但是flask内部实现了一个和threading.local()类似的功能~

源码

复制代码
from threading import local
import threading
import time

class Foo(object):
    def __init__(self):
        self.num = 0
# 当每个线程在执行 val1.xx=1 ,在内部会为此线程开辟一个空间,来存储 xx=1
# val1.xx,找到此线程自己的内存地址去取自己存储 xx,根据线程的id来进行区分线程。
val1 = local()
val = Foo()
def func(item):
    val1 = item
    val.num = val1
    time.sleep(3)
    # print(val.num)
    print(val1)

if __name__ == '__main__':
    for item in range(5):
        t = threading.Thread(target=func,args=(item,))
        t.start()
复制代码

 

 

 

 

 

 

 

 

 

 

 

 

 

 

pass

posted @   thep0st  阅读(172)  评论(0编辑  收藏  举报
(评论功能已被禁用)
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· AI与.NET技术实操系列(六):基于图像分类模型对图像进行分类
点击右上角即可分享
微信分享提示