路由、视图、模板配合才能完成较完整的请求响应过程。
前面简单了解了以上三者,这里将进一步介绍路由机制与视图。
就像在创建flask项目:Hello,Flask!与flask:flask模板——使用Jinja2中用过的那样,视图用于进行业务逻辑处理。
发现没有,这里的视图处理都是在一个函数中进行的,所以这个函数叫视图函数。视图函数完全由开发人员根据需求自定义开发,具有很高的灵活度。
然而这只是实现视图功能的一种方式,另一种在后面介绍。
URL需要使用路由装饰器进行注册:
@app.route('/') # 注册路由 def hello_world(): return 'Hello World!' 可以将多个URL注册到同一个函数视图。除了使用装饰器注册路由,还可以使用add_url_rule() app.py:
from flask import Flask, escape, url_for, redirect, render_template app = Flask(__name__) @app.route('/') def index(): return render_template('index.html') def url_test(): return '<h2>通过add_url_rule()过来</h2>' app.add_url_rule('/test/', endpoint='url_test', view_func=url_test) # 已注册URL规则的终结点。Flask本身假定视图函数的名称为端点,这个参数可以不使用 通过@app.route源码self.add_url_rule(rule, endpoint, f, **options)可以看出,它还是通过add_url_rule()实现的直接在URL最后添加 /<parameter——name>
@app.route('/user/<name>') def list_name(name): return "接收到的名字为: %s" % name可以添加一个转换器来为路由参数指定类型规则。
不指定这个规则时,默认使用string类型。
例如,指定int类型的参数:
@app.route('/news/<int:id>') def list_news(id): return "接收到的id为: %s" % id int 接受正整数float 接受正浮点数uuid 接受 UUID 字符串需要使用 methods 参数来处理不同的 HTTP 方法,默认的是GET。
from flask import request @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': return do_the_login() else: return show_the_login_form()到此为止,我们的URL都是在路由装饰器里面写死的,显然这不利于灵活使用URL,这就需要使用URL反转,它可以:
集中管理URL避免相对路径的使用错误给指定的函数构造 URL使用url_for() app.py:
from flask import Flask, escape, url_for app = Flask(__name__) @app.route('/') def index(): return url_for('blog', blog='xxxxxxxxxxx') @app.route('/user/blog/<blog>') def blog(blog): return '<h3>blog: {}</h3>'.format(blog) @app.route('/login') def login(): return 'login' @app.route('/user/<username>') def profile(username): return '{}\'s profile'.format(escape(username)) with app.test_request_context(): print(url_for('index')) print(url_for('login')) print(url_for('login', next='/')) print(url_for('profile', username='John Doe')) 第一个参数是函数视图名如果指定后面的参数,作为路由参数,否则作为查询使用使用 {{ url_for(‘function_name’)}} app.py:
from flask import Flask, escape, url_for, render_template app = Flask(__name__) @app.route('/') def index(): return render_template('index.html') @app.route('/login') def login(): return 'please login' @app.route('/user/<username>') def profile(username): return '{}\'s profile'.format(escape(username)) with app.test_request_context(): print(url_for('index')) print(url_for('login')) print(url_for('login', next='/')) print(url_for('profile', username='John Doe'))index.html:
{% extends "base.html" %} {% block title %}Index{% endblock %} {% block body %} <h4>welcome!</h4> <p><a href="{{ url_for('profile', username='John Doe') }}">user's profile</a></p> {% endblock %}页面重定向允许在某操作执行前或执行后跳转到别的页面执行别的操作,比如想要编辑博客内容,就应先跳转到登陆界面进行登录。
flask使用redirect()实现重定向。 app.py:
from flask import Flask, escape, url_for, redirect, render_template app = Flask(__name__) @app.route('/') def index(): url = url_for('show_index') return redirect(url) @app.route('/index') def show_index(): return render_template('index.html') @app.route('/user/<username>') def profile(username): return '{}\'s profile'.format(escape(username)) 访问http://127.0.0.1:5000/会跳转至http://127.0.0.1:5000/indexindex.html:
{% extends "base.html" %} {% block title %}Index{% endblock %} {% block body %} <h4>welcome!</h4> <p><a href="{{ url_for('profile', username='John Doe') }}">user's profile</a></p> {% endblock %}当然,在不同项目中开发功能重复的视图处理函数是相当枯燥的,虽然ctrl+c与ctrl+v解决了很多问题,但使用flask的类视图进行业务逻辑处理更有意思。
类视图是一类可插拔的视图,即用即插,它们已经实现了一些通用功能,具有很大的灵活性。
一个例子:
from flask import Flask, render_template, views app = Flask(__name__) class Ads(views.View): def __init__(self): super().__init__() self.context = { 'ads': '这是广告!' } class Index(Ads): def dispatch_request(self): self.context['title'] = '首页' print(self.context) return render_template('index.html', **self.context) class Login(Ads): def dispatch_request(self): self.context['title'] = '登录' return render_template('login.html', **self.context) class Register(Ads): def dispatch_request(self): self.context['title'] = '注册' return render_template('register.html', **self.context) app.add_url_rule(rule='/', endpoint='index', view_func=Index.as_view('Index')) app.add_url_rule(rule='/login/', endpoint='login', view_func=Login.as_view('login')) app.add_url_rule(rule='/register/', endpoint='register', view_func=Register.as_view('register')) if __name__ == '__main__': app.run(debug=True) Ads就是一个类视图,它必须继承自flask.views.View,其中self.context设置了模板上下文,其内容可直接在模板中使用。Index、Login、Register继承自Ads,它们必须实现dispatch_request方法,使用时必须在add_url_rule方法实现路由与视图的关联,其中rule定义URL规则,由view_func=class_view.as_view('urlname')完成关联。index.html:
{% extends "base.html" %} {% block title %}{{ title }}{% endblock %} {% block body %} <h1>{{ ads }}</h1> {% endblock %}login.html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{{ title }}</title> </head> <body> <h1>这是登录页面!</h1> <p><a href="{{ url_for('register') }}">请先注册</a></p> {{ ads }} </body> </html>register.html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{{ title }}</title> </head> <body> 这是注册页面!! {{ ads }} </body> </html>当然以上只是演示标准类试图的工作原理,但确实就可以使用不同类视图提供的功能了,然而实际上我们还会要求它们处理不同的HTTP方法,这需要在其中进行具体实现:
classMyView(View):methods=['GET','POST'] def dispatch_request(self): if request.method == 'POST': ...... app.add_url_rule('/myview',view_func=MyView.as_view('myview'))这还不够简练,flask提供了另一种类视图——基于方法的类视图,它可根据不同的HTPP请求类型执行不同的操作,要做的就是在实现类视图的时候将不同的处理分配给不同的方法:
class UserAPI(MethodView): def get(self): users=User.query.all() ... def post(self): user=User.from_form_data(request.form) ... app.add_url_rule('/users/',view_func=UserAPI.as_view('users'))一个例子: app.py:
from flask import Flask, render_template, request, views # 导入相应模块 app = Flask(__name__) # Flask初始化 @app.route('/') # 定义路由 def hello_world(): # 定义视图函数 return render_template('index.html') # 渲染模板 class LoginView(views.MethodView): # 定义LoginView类 # 当用户通过get方法进行访问的时候执行get方法 def get(self): # 定义get函数 return render_template("index.html") # 渲染模板 # 当用户通过post方法进行访问的时候执行post方法 def post(self): # 定义post 函数 username = request.form.get("username") # 接收表单中传递过来的用户名 password = request.form.get("pwd") # 接收表单中传递过来的密码 if username == 'admin' and password == 'admin': # 如果用户名和密码是否为admin return "用户名正确,可以登录!" # i f语句为真的话,返回可以登录信息 else: return "用户名或密码错误,不可以登录!" # 否则,返回不可以登录信息 # 通过add_url_rule添加类视图和url的映射关系 app.add_url_rule('/login', view_func=LoginView.as_view('loginview')) if __name__ == '__main__': # 当模块被直接运行时,代码将被运行,当模块是被导入时,代码不被执行 app.run(debug=True) # 开启调试模式index.html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style type="text/css"> .div1 { <!-- 定义div1容器-- > height: 180 px; <!-- 高度为180px-- > width: 380 px; <!-- 宽度为380px-- > border: 1 px solid #8A8989; <!-- 边框实线-- > margin: 0 auto; <!-- 使得元素水平对齐-- > } .input { <!-- 定义容器input-- > display: block; <!-- 让对象成为块级元素-- > width: 350 px; <!-- 宽度为350px-- > height: 40 px; <!-- 高度为40px-- > margin: 10 px auto; <!-- 使元素水平对齐-- > } .button {<!--定义容器button-- > background: #2066C5; color: white; font-size: 18 px; font-weight: bold; height: 50 px; border-radius: 4 px; } </style> </head> <body> <div class="div1"> <form action="login" method="post"><!--表单开始--> <input type="text" class="input" name="username" placeholder="请输入用户名"> <input type="password" class="input" name="pwd" placeholder="请输入密码"> <input type="submit" value="登录" class="input button"><!--定义登录submit--> </form> </div> </body> </html>更详细的内容请参考官方文档Pluggable Views