Web后端学习笔记 Flask(4)视图函数

Flask中的视图函数以及视图类:

添加视图函数还可以通过下面的方式add_url_rule进行:

app.add_url_rule(rule,  endpoint,  view_func):

rule:  对应的url

endpoint:相当于给url取一个名字

view_func:视图函数

-------------------------------------------------------------------------------------------------------------------------------
endpoint用于反转视图函数得到url,如果给endpoint赋了值,那么在使用url_for()获取视图函数的url时,就不能再使用函数名作为参数,而是应该使用endpoint来获取视图函数的url

from flask import Flask, url_for

app = Flask(__name__)

app.config["TEMPLATE_AUTO_RELOAD"] = True


@app.route('/')
def hello_world():
    print(url_for("theList"))   # 这里url_for的参数值只能是theList, 不能是my_list 
    return 'Hello World!'


def my_list():
    return "This is item list"


app.add_url_rule(rule="/list/", endpoint="theList", view_func=my_list)


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

标准类视图及其使用场景:

如何将类变成一个视图函数:类视图支持继承,写完类视图,需要通过add_url_rule进行添加

1. 标准类视图:

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

from flask import Flask, url_for, views

app = Flask(__name__)

app.config["TEMPLATE_AUTO_RELOAD"] = True


@app.route('/')
def hello_world():
    return 'Hello World!'


class ListView(views.View):
    def dispatch_request(self):
        """
        类视图中必须实现这一个方法
        :return:
        """
        return "This is list view"


app.add_url_rule(rule="/list/", endpoint="list", view_func=ListView.as_view("list"))
# 这里的as_view会将一个对象转化为函数,赋值给视图函数, 参数name是给转换后的函数取的名字


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

如果制定了endpoint,那么在使用url_for时必须使用endpoint, 如果没有指定,那么就可以使用as_view中的名字。

类试图的应用:

a. 例如有url需要返回字典

from flask import Flask, views, jsonify

app = Flask(__name__)

app.config["TEMPLATE_AUTO_RELOAD"] = True


@app.route('/')
def hello_world():
    return 'Hello World!'


class JsonView(views.View):    # 定义基类
    def get_data(self):
        return NotImplementedError

    def dispatch_request(self):
        return jsonify(self.get_data())


class ListView(JsonView):
    def get_data(self):
        return {"username": "Tom", "password": "2345"}


app.add_url_rule(rule="/list/", endpoint="list", view_func=ListView.as_view("list"))
# 这里的as_view会将一个对象转化为函数,赋值给视图函数, 参数name是给转换后的函数取的名字


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

JsonView类是基类,定义get_data()方法,ListView类是JsonView类的基类,在基类中,重新实现了get_data()方法,在调用ListView中的dispatch_request()方法的时候,由于ListView中未重写dispatch_request()方法,所以回去调用父类中的dispatch_request()方法,在父类的dispatch_request()方法中会调用get_data()方法,而在子类ListView中实现了get_data方法,所以实际调用的是子类ListView中的方法,返回字典

b. 有几个url需要返回相同的变量:

例如,在登陆页面以及注册页面,需要展示相同的广告,则可以通过下面继承的方法实现:

from flask import Flask, views, render_template

app = Flask(__name__)

app.config["TEMPLATE_AUTO_RELOAD"] = True


@app.route('/')
def hello_world():
    return 'Hello World!'


class AdsView(views.View):   # 广告内容
    def __init__(self):
        super(AdsView, self).__init__()   # 调用父类的init方法
        self.ad_content = {
            "product": "GMCC-1001"
        }

    def dispatch_request(self):
        return NotImplementedError     # 因为基类中的这个方法不会被调用


class LoginView(AdsView):
    def dispatch_request(self):
        return render_template("html/login.html", **self.ad_content)


class RegisterView(AdsView):
    def dispatch_request(self):
        return render_template("html/registry.html", **self.ad_content)


app.add_url_rule(rule="/login/", endpoint="login", view_func=LoginView.as_view("login"))
app.add_url_rule(rule="/registry/", endpoint="registry", view_func=RegisterView.as_view("registry"))


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

通过这种方法,可已将相同的广告内容展示在不同的页面。且修改起来更加的容易。

2. 基于请求方法的视图

Flask还提供了另一种类视图,flask.view.Methodview,对每个http执行不同的函数(映射到对应方法的小写的同名方法上),这对restful API尤其有用。

基于方法的类视图,是根据请求的“method”不同来执行不同的方法的,如果用户是发送的“get”请求,那么将会执行这个类的get方法,如果发送的是post请求,将会执行类的post方法。其他的“method”也类似,例如delete,put方法

from flask import Flask, views, render_template, request

app = Flask(__name__)

app.config["TEMPLATE_AUTO_RELOAD"] = True


@app.route('/')
def hello_world():
    return 'Hello World!'


class LoginView(views.MethodView):
    def get(self, error=None):
        return render_template("html/login.html", error=error)

    def post(self):
        """
        获取post请求提交的参数
        :return:
        """
        username = request.form.get("username")
        password = request.form.get("password")
        if username == "knight" and password == "123456":
            return self.get(error="success")
        else:
            return self.get(error="fail")


app.add_url_rule(rule="/login/", view_func=LoginView.as_view("login"))


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

前端代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登陆</title>
</head>
<body>
<form action="" method="post">
    <table>
        <tr>
            <td>用户名</td>
            <td><input type="text" name="username"></td>
        </tr>
        <tr>
            <td>密码</td>
            <td><input type="password" name="password"></td>
        </tr>
        <tr>
            <td></td>
            <td><input type="submit" value="点击登陆"></td>
        </tr>
    </table>
    {% if error %}
        {% if error == "success" %}
        <p style="color: green">登陆成功</p>
        {% else %}
            <p style="color: red">登陆失败</p>
        {% endif %}
    {% endif %}
</form>
</body>
</html>

 

类视图中使用装饰器:

1. 如果使用的是函数视图,那么自己定义的装饰器必须放在“app.route()”装饰器的下面才能生效。否则这个装饰器不能起作用。

2. 类视图的装饰器需要重写类视图的一个属性decorates,这个类属性是一个列表或者元组,里面装的就是所有的装饰器。

应用场景:例如网站上的个人信息页面和设置页面,在跳转到这些页面的时候,如果用户没有登陆,则会自动跳转到登陆页面,

提醒用户登陆。只有在登录状态下才能访问这些页面,下面通过装饰器模拟这一功能。

from flask import Flask, views, render_template, request
from functools import wraps

app = Flask(__name__)

app.config["TEMPLATE_AUTO_RELOAD"] = True


def login_required(func):    # 定义装饰器
    @wraps(func)    # 保留参数 函数func的一些属性,如__name__属性等
    def wrapper(*args, **kwargs):
        username = request.args.get("username")
        if username and username == "Tom":
            return func(*args, **kwargs)
        else:
            return "请先登陆"
    return wrapper


@app.route('/')
def hello_world():
    return 'Hello World!'


@app.route('/setting/')
@login_required        # 自定义的装饰器应该放在url的下main
def setting():
    return "User setting"


# 给类视图添加装饰器
class ProfileView(views.View):
    decorators = [login_required]

    def dispatch_request(self):
        return "这是个人页面"


app.add_url_rule("/profile/", view_func=ProfileView.as_view("profile"))


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

在这些页面中,只有请求参数中有用户名参数的时候,才能够跳转到相应的页面,否则会提示登陆。

蓝图的基本使用:

蓝图用于将一个大型Flask项目分层解耦,进行模块化划分,可以将相同模块的视图函数放在同一个文件中,统一进行管理

例如,网站主要可以划分为新闻,电影,图书,个人中心四个模块,则可以按照如下的方式进行划分:

user.py文件:(初始化蓝图)

# -*- coding: utf-8 -*-
from flask import Blueprint

user_bp = Blueprint("user", __name__, url_prefix="/user")    # 初始化蓝图


# 个人中心
@user_bp.route('/profile/')
def profile():
    return "个人中心页面"


@user_bp.route("/settings/")
def settings():
    return "设置页面"


news.py文件 (初始化蓝图), 同时在news.py中渲染html文件的方法不变:

# -*- coding: utf-8 -*-
from flask import Blueprint, render_template

news_bp = Blueprint("news", __name__, url_prefix="/news")


@news_bp.route("/news_list/")
def news_list():
    return render_template("html/news_list.html")


@news_bp.route("/news_detail/")
def news_detail():
    return "新闻详情"

在app.py文件中,注册上面创建的蓝图

from flask import Flask, views, render_template, request
from blueprints.user import user_bp
from blueprints.news import news_bp

app = Flask(__name__)
app.register_blueprint(user_bp)
app.register_blueprint(news_bp)

app.config["TEMPLATE_AUTO_RELOAD"] = True


@app.route('/')
def hello_world():
    return 'Hello World!'


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

这样就将项目中的视图函数分模块进行管理。

url_prefix:url前缀,如果加了,则视图函数对应的url前面必须有前缀,且只能有一个斜杠

route中还可以指定template_folder参数,如果制定了这个参数template_folder=“xxx”,那么flask在渲染模板的时候,首先会去项目中的templates文件夹中寻找,如果没有找到,flask接下来会去blueprints/xxx文件夹中寻找模板文件。

蓝图中静态文件的寻找规则:

1. 在模板文件中,加载静态文件,如果使用url_for('static'),那么就会在app指定的静态文件夹目录下查找静态文件

2.如果在加载静态文件的时候,指定了蓝图的名字,比如“news.static”,那么Flask就会到这个蓝图指定的static_folder下查找静态文件。

url_for反转蓝图注意事项:

如果需要跨文件反转视图函数,则需要指定视图函数的蓝图名字,再加上视图函数名字,例如:

@app.route('/')
def hello_world():
    print(url_for("news.news_list"))
    return 'Hello World!'

例如,需要反转news_list视图函数,则需要在加上其蓝图的名字才可以,在模板中和在python文件中的用法一样,同样需要加蓝图的名字。

即使在同一个蓝图中反转视图函数,也需要指定蓝图的名字。

子域名实现详解:

1. 使用蓝图技术

2. 在创建蓝图对象的时候,需要传递一个"subdomain"参数,来指定这个子域名的前缀:

3. 需要在主app文件中,需要配置serve name参数:

【注】IP地址不能有子域名,localhost不能有子域名

例如,CMS子域名:

# -*- coding: utf-8 -*-
from flask import Blueprint

cms_bp = Blueprint("cms", __name__, subdomain="cms")


@cms_bp.route("/")
def index():
    return "Cms index Page"

在app.py文件中设置:

from blueprints.cms import cms_bp

app.register_blueprint(cms_bp)

app.config["TEMPLATE_AUTO_RELOAD"] = True
app.config["SERVER NAME"] = "jd.com:5000"
# 修改host文件:
# 127.0.0.1 jd.com
# 127.0.0.1 cms.jd.com

此时设置完毕计算机的host文件后,在本地计算机访问jd.com,就会映射到127.0.0.1

---------------------------------------------------------------------------------------------------------------------------------------

posted @ 2020-04-08 11:05  Alpha205  阅读(200)  评论(0编辑  收藏  举报