Flask框架的学习——05—(类视图、基于调度方法的视图、权限验证、蓝图、url_prefix参数的使用、寻找静态资源文件、寻找静态模板文件、url_for生成url)

1、类视图

之前我们接触的视图都是函数,所以一般简称视图函数。其实视图也可以基于类来实现,类视图的好处是支持继承,但是类视图不能跟函数视图一样,写完类视图还需要通过app.add_url_rule(url_rule,view_func)来进行注册

1.1 标准类视图

标准类视图是继承自flask.views.View,并且在子类中必须实现dispatch_request方法,这个方法类似于视图函数,也要返回一个基于Response或者其子类的对象。

# -*- encoding: utf-8 -*-
"""
@File    : view_demo.py
@Time    : 2020/4/16 10:33
@Author  : chen

"""
from flask import Flask, render_template, url_for, request
from flask import views, jsonify

app = Flask(__name__)


@app.route("/")             # 实际相当于调用了add_url_rule()方法实现的
def view_demo():
    # 如果add_url_rule 给了endpoint,相当于给url起了一个名字,endpoint='name'
    print(url_for("name"))     # 这里的url_for("name")必须和endpoint='name'相同才能实现url反转的功能
    return "首页"


def profile():
    return "个人中心"


# 类视图   必须重写dispatch_request()方法
class ListView(views.View):         # 这里必须写views.View
    def dispatch_request(self):     # 必须重写dispatch_request()方法,否则会主动抛出异常  源码中:raise NotImplementedError()
        return "类视图"
    
    def demo(self):                  # self.demo()进行调用该方法
        return "测试"
# *********************************************************************************************************


# 返回json类型数据
class JsonView(views.View):
    def dispatch_request(self):
        response = self.get_response()          # 调用子类中重写的方法,将返回数据传入jsonify函数中
        return jsonify(response)                # jsonify(response)将数据返回成json数据类型
    
    def get_response(self):
        raise NotImplementedError()            # 主动抛出异常,代表在子类中必须要重写该方法


# 继承了类视图JsonView,就不用重写dispatch_request方法
class ListJsonView(JsonView):
    def get_response(self):
        return {'username': "xxxx"}           # 子类中重写了get_response方法,返回数据至调用方法的父类中

# *********************************************************************************************************


# 添加url规则
app.add_url_rule('/profile/', endpoint='name', view_func=profile)            # profile  注意不能加()
# 优先使用endpoint中命名的名称,然后再使用view_func命名的名称
app.add_url_rule('/list/', endpoint='list', view_func=ListView.as_view('list'))
app.add_url_rule('/listjson/', view_func=ListJsonView.as_view('listjson'))      # 注意类名   子类名


if __name__ == '__main__':
    app.run(debug=True)
add_url_rule注意事项:

第一个’/login/'是URL地址 ,第二个as_view(‘login’)是方法名,通过url地址来执行as_view(‘login’)的方法。
且优先使用endpoint中命名的名称,然后再使用view_func命名的名称

# 第一个'/login/'是url地址    第二个as_view('login')是方法名
app.add_url_rule('/login/', endpoint='list', view_func=LoginView.as_view('login'))

`

有时候不同项目开启会有缓存文件,此时开启其他的项目的时候就需要清空缓存文件,可以用快捷键:
Ctrl+F5或者Ctrl+Shift+R就可以清空缓存文件,再开启下一个项目。

`
标准类视图另一个功能:把公有部分的数据抽离出来,放在父类中,由子类继承之后传递

# -*- encoding: utf-8 -*-
"""
@File    : methods_demo.py
@Time    : 2020/4/16 20:32
@Author  : chen

"""

from flask import Flask, render_template, url_for, request
from flask import views, jsonify

app = Flask(__name__)


# 返回公共的变量
class BaseView(views.View):
    def __init__(self):
        super().__init__()          # 调用父类中的方法
        
        self.context = {            # 把公有的数据抽离出来,放在父类中,由子类继承之后传递
            "name": "123"           # context传数据到html模板里,用name传
        }


class LoginView(BaseView):
    def dispatch_request(self):
        return render_template("login.html", **self.context)


# 返回公共的变量
class RegistView(BaseView):
    def dispatch_request(self):
        return render_template("regist.html", **self.context)

    
app.add_url_rule('/login/', view_func=LoginView.as_view('login'))
app.add_url_rule('/regist/', view_func=RegistView.as_view('regist'))         # 注意类名   子类名


if __name__ == '__main__':
    app.run(debug=True, port=8888)

1.2 基于调度方法的视图

Flask还为我们提供了另外一种类视图flask.views.MethodView,对每个HTTP方法执行不同的函数(映射到对应方法的小写的同名方法上)

from flask import Flask, render_template, url_for, request
from flask import views
from flask.views import MethodView

app = Flask(__name__)


class LoginView(views.MethodView):
    # def rend_temp(self, *args, **kwargs):                   # 代码继续封装,将rend_template封装成单独的方法,方便调用
    #     return render_template('login.html',)
    
    def get(self, error=None):
        return render_template('login.html', error=error)         # 进行封装

    def post(self):
        name = request.form.get("name")
        password = request.form.get("password")
        
        # 此时这一步需要登录数据库,验证账号密码正确性
        if name == "123" and password == "123":
            return "登陆成功"
        else:
            # return "账号或者密码错误,登陆失败!"
            # return render_template("login.html", error="账号或者密码错误")     # 如果账号或者密码错误则传递一个参数error到模板文件login.html中
            return self.get("账号或者密码错误")                # 把上面的返回语句封装到get函数中


# 添加url规则   必须要有   第一个'/login/'是url地址    第二个as_view('login')是方法名
app.add_url_rule('/login/', view_func=LoginView.as_view('login'))


if __name__ == '__main__':
    app.run(debug=True, port=8888)

在这里插入图片描述

1.3 权限验证

用类视图的一个缺陷就是比较难用装饰器来装饰,比如有时候需要做权限验证的时候

# 登陆之后才能访问的装饰器
def login_required(func):
    def wrapper(*args, **kwargs):
        print(type(request.args))       # <class 'werkzeug.datastructures.ImmutableMultiDict'>
        print(type(request))            # <class 'werkzeug.local.LocalProxy'>

        username = request.args.get("username")       # 从url中获取username参数

        if username:
            return func(*args, **kwargs)             
            # 返回fun参数和不确定参数,如果方法wrapper中没写(*args, **kwargs),这里也可以不用写,如果只是返回函数func,就相当于返回调用下面的seting()方法
        else:
            return "请先登录"
    return wrapper                             # 返回内层函数wrapper,(*args, **kwargs)是不确定参数


@app.route("/settings/")           # 装饰路由
@login_required                   # 装饰器   封装功能为了限制登录  顺序不能换
def seting():
    return "个人中心设置"

**上面的双装饰器功能:

  • return func(*args, **kwargs)
    返回fun函数和不确定参数,如果方法wrapper中没写(*args, **kwargs),这里也可以不用写,如果只是返回函数func,就相当于返回调用下面的seting()方法

  • @app.route("/settings/")
    装饰路由

  • @login_required
    装饰器,封装功能为了限制登录 ,顺序不能与装饰路由换

装饰器写完后,可以在类视图中定义一个属性叫做decorators,然后存储装饰器。以后每次调用这个类视图的时候,就会执行这个装饰器

class ProfileView(views.View):
    # 在类中用装饰器,需要用decorators属性
    decorators = [login_required]                 # 调用login_required方法当成装饰器
    
    def dispatch_request(self):
        return "个人中心"
    

app.add_url_rule('/proview/', view_func=ProfileView.as_view('proview'))

2、蓝图(重要部分)

之前我们写的url和视图函数都是处在同一个文件,如果项目比较大的话,这显然不是一个合理的结构,而蓝图可以优雅的帮我们实现这种需求。

主文件app.py的实现。

from flask import Flask
from blueprints.news import news_bp             # 导入news.py模块    news_bp相当于下面的app
from blueprints.books import book_bp

app = Flask(__name__)
app.register_blueprint(news_bp)                 # news_bp进行注册
app.register_blueprint(book_bp)                 # book_bp进行注册


@app.route("/")
def index():
    return "首页"


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

蓝图文件:news.py模块

from flask import Blueprint


news_bp = Blueprint('news', __name__)


@news_bp.route("/news/")
def news():
    return "新闻页面显示"

蓝图文件:books.py模块

from flask import Blueprint


book_bp = Blueprint('books', __name__)


@book_bp.route("/books/")
def book_list():
    return "图书页面显示"


@book_bp.route("/books/detail/<bid>")
def book_detail(bid):
    return "图书ID %s " % bid

以后访问/news/,/books/detail/,都是执行的blueprint_demo_flask.py文件中的视图函数,这样就实现了项目的模块化。

url_prefix参数的使用
from flask import Blueprint


book_bp = Blueprint('books', __name__, url_prefix="/books/")     # 当url_prefix修改过后


@book_bp.route("/")                  # 当上面的url_prefix修改过之后,这里的路由默认/books/开头
def book_list():
    return "图书页面显示"


@book_bp.route("/detail/<bid>")      # 当上面的url_prefix修改过之后,这里的路由默认/books/开头
def book_detail(bid):
    return "图书ID %s " % bid
from flask import Blueprint

# url_prefix路由是从news/开始的
news_bp = Blueprint('news', __name__, url_prefix="/news/")


# /news/   url_prefix    ----->   /news/
@news_bp.route("/")                              # 当上面的url_prefix修改过之后,这里的路由就不用了写了,默认/news开头
def news():
    return "新闻页面显示"

3、寻找静态资源文件

默认不设置任何静态文件路径,Jinja2会在项目的static文件夹中寻找静态文件。也可以设置其他的路径,在初始化蓝图的时候,Blueprint这个构造函数,有一个参数static_folder可以指定静态资源文件的路径

# url_prefix路由是从news/开始的    template_folder="test_template_mudel" 相对路径,相对于当前文件news.py来说,创建的文件要和news.py在同一路径
news_bp = Blueprint('news', __name__, url_prefix="/news/", template_folder="test_template_mudel", static_folder='static')
# static_folder='static'路径是相当对于当前文件news.py的相对路径  即在'/blueprints/static/'路径下查找资源文件
# 'news'为蓝图名称

static_folder可以是相对路径(相对蓝图文件所在的目录),也可以是绝对路径。在配置完蓝图后,还有一个需要注意的地方是如何在模板中引用静态文件。在模板中引用蓝图,应该要使用蓝图名+.+static来引用

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

    <!--  加载静态资源文件  默认加载的路径是 'blueprint_flask_demo/static/news.css'  -->
    <!--    <link rel="stylesheet" href="{{ url_for('static', filename='news.css') }}">-->


    <!--  加载静态资源文件  修改后的加载的路径是 'blueprint_flask_demo/blueprints/static/news.css'  -->
    <link rel="stylesheet" href="{{ url_for('news.static', filename='news.css') }}">
    <!-- 使用蓝图名+.+static来引用, 即'news.static' -->

</head>
<body>
    <h1>这是templates中的新闻首页</h1>
</body>
</html>

寻找静态模板文件html

跟静态资源文件一样,默认不设置任何模板文件的路径,将会在项目的templates中寻找模板文件。也可以设置其他的路径,在构造函数Blueprint中有一个template_folder参数可以设置模板的路径

from flask import Blueprint, render_template

# url_prefix路由是从news/开始的    template_folder="test_template_mudel" 相对路径,相对于当前文件news.py来说,创建的文件要和news.py在同一路径
news_bp = Blueprint('news', __name__, url_prefix="/news/", template_folder="test_template_mudel")


# /news/   url_prefix   ----->   /news/
@news_bp.route("/")                   
def news():
    # return "新闻页面显示"

    # 查找模板文件会先根据 template_folder="test_template_mudel"这个路径去寻找,找不到之后再查找默认路径templates/news.html
    return render_template("news.html")

查找模板文件会先根据默认路径templates下寻找模板文件news.html,找不到模板html文件之后, 会根据template_folder="xxxxxx"这个路径进行查找模板文件

4、url_for生成url:

用url_for生成蓝图的url,使用的格式是:蓝图名称+.+视图函数名称。比如要获取admin这个蓝图下的index视图函数的url。

from flask import Flask, url_for
from blueprints.news import news_bp             # 导入news.py模块    news_bp相当于下面的app
from blueprints.books import book_bp

app = Flask(__name__)
app.register_blueprint(news_bp)                 # news_bp进行注册
app.register_blueprint(book_bp)                 # book_bp进行注册


@app.route("/")
def index():
    
    print(url_for("news.news"))          # 反转url   news.是根据Blueprint('news')中的第一个参数来确定的 后一个是写方法名
    print(url_for("books.book_detail", bid=5))  # 反转url   book_detail是方法名称   bid=5是book_detail方法中需要传的参数
    
    return "首页"


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

反转url : 第一个news.是根据Blueprint(‘news’)中的第一个参数来确定的,后一个news是写方法名。
反转url : book_detail是方法名称,bid=5是book_detail方法中需要传的参数。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值