Flask-2-路由

前沿:处理URL和视图函数之间的绑定关系的程序就叫路由

一、多个URL路由

一个函数可以设置多个URL路由规则

@app.route("/cases")
@app.route("/cases/<case_id>")
def get_case(case_id=None):
    if case_id is not None:
        return f"{case_id}"
    return "cases"

这个例子接收两种URL,通过任何一个URL都可以走到这个视图函数里面

  • 访问localhost:5000/cases:则返回cases
  • 访问localhose:5000/case/432423,则返回432423

注意

1 、多个url可以绑定同一个视图函数,但是切记同一个url不能绑定在多个视图函数上

2、如果还有其他的装饰器怎么处理?

  • 视图装饰器应该放最外层,否则里面的装饰器不会生效
  • 视图函数包裹的装饰器不要return其他信息,只要返回我们视图函数即可,否则会被包装程返回数据。
def log_time(func):
    def swapper(*args, **kwargs):
        print(time.time())
        # 千万不要return其他值,否则会被包装成响应对象,返回到前端页面
        # return "自定义装饰器"
        return func(*args, **kwargs)

    return swapper


@app.route("/interface/")
@log_time
def get_interface():
    return "interface"
    

# 注意前面说的自定义的装饰要放在url注册装饰器的下面,
# 如果位置换一下,你会发现自动义装饰器不会执行

装饰器就是在视图函数外,处理一些公共的方法,比如储存访问的次数

def set_count(func):
    def swapper(*args, **kwargs):
        app.config["num"]+=1
        return func(*args, **kwargs)
    return swapper

二、路由的动态参数

我们在设计url的时候,也可以设置成动态的url,前端通过url来传参给我们,不一定非要通过get,post请求传参

只需要把动态的url放在<>中如:@app.route("/cases/<case_id>"),他可以把前端访问的url动态部分,传到视图函数中,视图函数需要设置一个参数接收即可

@app.route("/cases/<case_id>")
def get_case(case_id=None):
    if case_id is not None:
        return f"{case_id}"
    return "cases"

动态url参数接收类型

# int:整数
@app.route("/cases/<int:case_id>")

# float:小数
@app.route("/cases/<float:case_id>")

# string: 字符串,没有定义时默认类型
@app.route("/cases/<case_id>")
@app.route("/cases/<string:case_id>")

# path:路径 /34/2233/4324/,可以/收尾,其他的不能
@app.route("/cases/<path:case_id>")

三、路由重定向

比较访问,/index,和/index/ 两个路由之间的区别

1、/index :我们访问/index/的时候不能访问成功,会报错404

flask 哲学:当url 定义成/interface时, flask它会认为 /interface 和 /interface/ 是两个不同的url。
定义的/interface,不能通过/interface/访问。

2、/index/:我们定义这个时候,访问/index和/index/都能访问成功,但是两种是有区别的

flask 为了灵活 有另外一种方式兼容:永久重定向。
即:可以定义成/index/,此时 访问/index,flask没有找到,会告诉游览器,我找到了一个接近的 返回308,找到的url
定义在location中,然后游览器在访问 location中的 /index/ ,返回200.

3、为什么不进行这样的操作呢?

唯一url原则

  • 从逻辑上来讲这样是不合理的,我们同样一个逻辑,你为什么要给两个地址?
  • 对SEO有好处,两个url会被搜索两次,重复内容会被降权
  • 到底后面加“/”,还是不加呢?加可以兼容,不加就报错,根据自己实际需求处理就好,我通常是不加的

四、路由注册机制

访问URL执法对应的视图函数,函数是没有主动调用的,在哪里使用的呢?

之前我们最小原型实例中,优化后我们是把url和函数处理逻辑映射到字典中保存,其实flask也是这一套操作

flask其实提供了两种方式注册路由

1、装饰器:@app.route("/")

@app.route("/")
def index():
    pass

2、集中注册:app.add_url_rule()


def get_case_list():
    return "case_list"

app.add_url_rule("/case_list", view_func=get_case_list)

3、源码阅读

其实我们看route()方法源码,也可以发现也是调用app.add_url_rule(),只是flask帮我实现了装饰器,在中小型项目中更加优雅方便

而url和视图的映射关系(通过端点-下面有提到)都是保存在url_map里面

4、装饰器VS集中注册

装饰器更加优雅,看起来更加清晰,url和视图函数关系明确,非常适合中小型项目

集中注册,更加灵活,把所有的url路由都定义到了一起,当某一个路由出现出现问题的适合,很快就能找到改路由,不能到各个视图函数文件中找,适合中大型项目

提示:其实falsk提供的所有装饰器,都有对应的集中注册方法,后续降到模板引擎用的和请求钩子等都可以采用集中方式,这里大家有个印象即可,后续分享到对应内容在具体说明

五、app.route()可配置参数

app.route()的option参数是用了werkzeug的Rule类

有很多参数可配置,也是我们经常会用的

我们主要掌握其中几个重点经常用的参数即可,其他如有需求,请自行阅读源码的参数说明进行使用

1、endpoint:端点

可以理解为url和视图函数绑定关系的命名,默认是None,flask会取视图函数的名称定义关系名,也可以自定义

进行URL构建的时候会用url_for(endpoint),endpoint的主要作用是在url和视图函数中间设置一个桥梁,通过这个endpoint就能找到对应的url和视图函数

@app.route("/home", endpoint="get_home_1")
def get_home():
    return "home"

print(app.url_map())

2、methods:请求方法

默认没有设置的时候,仅支持GET、HEAD、OPTIONS
我们就通过这个参数来限制前端的接口的访问方法

# 定义post请求方法,前端访问只能用post方法请求
@app.route("/login/<username>", methods=["POST"])
def login(username=None):
    if username is None:
        return "请输入用户名"
    return "登录成功"

3、redirect_to:重定向

我们在使用重定向的时候其实是有两种方式的,一是用redirect_to关键字参数,二是用redirect()方法实现。

两种究竟有何不同呢?

一、redirect_to关键字参数

使用关键字参数重定向到其他路由的时候,本视图函数的逻辑是不会执行的,只会执行重定向的路由的视图函数逻辑

@app.route("/cases/<path:case_id>")
def get_case(case_id=None):
    if case_id is not None:
        print(type(case_id))
        return f"{case_id}"
    return "cases"
    
# 当时定义参数redirect_to 参数时,访问"/" 不在返回index,返回重定向的路由case_id
@app.route("/",methods=["GET"],redirect_to="/cases/4444")
def index():
    print("关键字参数重定向内部逻辑")
    return "index"

二、redirec():方法

flask给我给提供了重定向其他路由的方法,接收url参数即可。

但是url这种参数有可能会变,而url和视图函数之间的绑定关系一般是不变的,也就是端点一般是不变的,所以redirect()方法配合url_for(endpoind)使用

1)url_for()

一般重定向的时候组合使用,url_for方法,传入端点名,它里面就帮我们实现了,通过端点找到url,而且还支持传关键字参数,假如重定向到interface接口时,需要一个关键字参数id=3,可以直接redirect(url_for("interface",id=3))当然直接使用url在url后面加?id=3 也可行

2)参数说明

endpoint:端点(默认函数的名字)

**values:URL的关键字参数

_external:如果设置为True,则生成一个绝对路径URL

_scheme:一个字符串指定所需的URL方案。_external参数必须设置为True,不然会抛出ValueError。

_anchor:如果设置了这个则给URL添加一个mao

_method: 如果设置这个则显示地调用这个HTTP方法

3)redirect+url_for案例

@app.route("/interface/")
def get_interface():
    return "interface"
    

# 方式二,在视图函数内处理完逻辑在重定向
@app.route("/home")
def get_home():
    print("函数内部逻辑")
    # 得到完整的url,并且请求参数id=3
    print(url_for("get_home_1", _external=True, id=3))
    return redirect(url_for("get_interface", id=3))

4、defaults:参数默认值

定义默认参数,可以在视图函数中接收,defaults={"id":2}

也可以直接在视图函数中定义默认值参数def interface(id=2) ---更方便

@app.route("/cases/<case_id>")
def get_case(case_id):
    return f"{case_id}"

如果我们请求的时候不传case_id 就会报错

怎么才能调整他不报错呢?

我们只要给路由增加一个defaults默认值即可

@app.route("/cases/", defaults={"case_id": 3})
@app.route("/cases/<case_id>")
def get_case(case_id):
    return f"{case_id}"

我们也可以不同这个参数,可以直接在视图函数中定义参数的给默认值即可--更方便

@app.route("/cases/")
@app.route("/cases/<case_id>")
def get_case(case_id=3):
    return f"{case_id}"

六、视图函数的分离(大型项目)

随着项目的增大,我们想要把视图函数放在一起,视图函数,app、url不写在一个文件里的,所以我们的架构变成了

  • 启动文件:仅仅初始化app,配置信息,启动服务
  • 视图函数:接收请求,处理数据、响应结果
  • url集中注册:url和视图绑定
  • 数据处理:为视图函数服务,被视图函数调用
  • templates:html页面
  • static:静态文件如js,css
  • 其他的帮助函数:其他公共方法
# viewfunction.py 视图函数py文件

# form model层的数据处理方法
# 首页
def home():
    # 接收请求数据
    # 处理数据
    return "home page"


# cases页
def cases():
    return "case page"
# urls.py
# 导入app对象、导入视图函数,进行集中对象
#

from flask_main import app
import viewfunction as views

# 注册路由
app.add_url_rule("/", view_func=views.home)
app.add_url_rule("/cases", view_func=views.cases)
# flask_main.py
# 主要时初始化app,处理配置信息,开启服务即可
# 接下来导入urls.py注册的路由即可

from flask import Flask

# from flask_frame.Flask_views.urls import *  # 直接引用会导致循环导入

app = Flask(__name__)
app.config["DEBUG"] = True

# 只能在哪里用的时候在导入
from flask_frame.Flask_route.flask_fenceng.urls import *

if __name__ == '__main__':
    app.run(debug=app.config.get("DEBUG"))

上述只简单的分了视图、url、app分离,还有具体model层和其他公共方法之类根据业务实际分层即可

这样我们主入口功能其实非常明确的,也非常的简单

注意

不知道大家注意到没有?在我们的urls层需要到flask.main文件的app 来注册路由,而falsk.mian里面又需要加载urls层注册的路由,然后开启的服务才能加载上这些路由。

两个文件之间互相导入,肯定会导致循环导入的情况发生。

所以为了避免循环导入我们只能像案例中的那样,哪里用就在哪里导入,没必要非要遵守PEP8规范。其实很多三方框架都是这么处理,包括falsk也用过这种导入

posted @ 2020-06-10 23:02  讲明白  阅读(1314)  评论(0编辑  收藏  举报