视图函数作用即生成请求的响应,如果把业务逻辑和表现逻辑混在一起会导致代码难以理解和维护。吧表现逻辑转移到模板中能够提升程序的可维护性。
模板是一个响应文本的文件,其中包含用占位变量表示的动态部分,其具体值只在请求的上下文才能知道。
使用真实值替换变量,在返回最终得到的响应字符串,这一过程称为渲染。
在默认情况下,Flask程序会在templates子文件夹中寻找模板。在下一个hello.py版本中,要把前面定义的模板保存在templates文件夹中,并分别命名为index.html和user.html。
from flask import Flask,render_template
from flask_script import Manager
app = Flask(__name__)
manager = Manager( app )
@app.route('/')
def index():
return render_template('index.html')
@app.route('/user/<name>')
def user(name):
return render_template('user.html',name=name)
if __name__ == "__main__":
manager.run()
Jinja2能识别所有类型的变量,甚至是一些复杂的类型,例如列表、字典和对象。在模板中使用变量的一些示例如下:
<p>DICT {{ mydict['key'] }}</p>
<p>LIST {{ mylist[3] }}</p>
<p>list with a variable index: {{ mylist[myintvar]}}</p>
<p> object's method: {{ myobj.somemethod() }} </p>
条件控制语句
{% if user %}
{% else %}
{% endif %}
for循环语句
{% for comment in comments %}
{% endfor %}
支持宏
{% marco render_comment(comment) %}
多处重复使用的模板代码片段可以写入单独的文件,再包含在所有的模板中,以避免重复:
{ % include 'comment.html' %}
<html>
<head>
{% block head %}
<title>
{% block title %}
{% endblock %}
- My Application
</title>
{% endblock %}
</head>
<body>
{% block body %}
{% endblock %}
</body>
</html>
{% extends bash.html %}
{% block title %}
Index
{% endblock%}
{% block head%}
{{ super() }}
<style>
</style>
{% endblock %}
{% block body %}
<h2>hello,world</h2>
{% endblock %}
Bootstrap是Twitter开发的一个开源框架,它提供的用户界面组件可用于创建整洁且具有吸引力的网页,并且这些网页还能兼容所有现代的Web浏览器。
Bootstrap是客户端框架,不会直接涉及服务器。要下在程序中继承Bootstrap,显然需要对模板做所有必要的改动,更简单的方法就是安装Flask-Bootstrap的Flask扩展,简化集成的过程。
pip install flask-bootstrap
Flask扩展一般在创建程序实例时初始化。
from flask.ext.bootstrap import Bootstrap
bootstrap = Bootstrap(app)
{% extends "bootstrap/base.html" %}
{% block title %}Flasky{% endblock %}
{% block navbar %}
<div class="navbar navbar-inverse" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle"
data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">Flasky</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li><a href="/">Home</a></li>
</ul>
</div>
</div>
</div>
{% endblock %}
{% block content %}
<div class="container">
<div class="page-header">
<h2>Hello, {{ name }}!</h2>
</div>
</div>
{% endblock %}
代码详解:
Flaks-Bootstrap基模板中定义的块:
块名 说明
- doc 整个HTML文档
- html_attribs <html>标签的属性
- html <html>标签的内容
- head <head>标签中的内容
- title <title>标签中的内容
- metas 一组<meta>标签
- styles 层叠样式表定义
- body_attribs <body>标签的属性
- body <body>标签中的内容
- navbar 用户定义的导航条
- content 用户定义的页面内容
- scripts 文档底部的JavaScript声明
Flask允许程序使用基于模板的自定义错误页面,最常见的错误代码有两个:
404,客户端请求未知页面或路由时显示。
500,有未处理的异常时显示。
@app.errorhandler(404)
def page_not_found(e):
return render_template('404.html'),404
@app.errorhandler(500)
def internal_server_error(e):
return render_templte('500.html'),500
在模板中直接编写简单路由的URL连接不难,但对于包含可变部分的动态路由,在模板中构建正确的URL就很困难;并且直接编写URL会对代码中定义的路由产生不必要的依赖。
Flask提供了url_for()辅助函数,可以使用程序URL映射中保存的信息生成URL。
url_for()函数最简单的用法是以视图函数名(后者app.add_url_route()定义路由时使用的端点名)作为参数,返回对应的URL。
使用url_for()生成动态地址时,将动态部分作为关键字参数传入。例如,url_for('user',name='john',_external=True)的返回结果是http://localhost:5000/user/john
传入url_for()的关键字参数不仅限于动态路由中的参数。函数能将任何额外参数添加到查询字符串中。例如,url_for('index',page=2)的返回结果是/?page=2
默认设置下,Flask在程序根目录中名为static的子目录中寻找静态文件。如果需要,可在static文件夹中使用子文件夹存放文件。
问题背景:如果Web程序的用户来自世界各地,那么处理日期和时间就不是一个简单的任务。
解决方法:通过使用JavaScript开发的优秀客户端开源代码库,名为moment.js,可以在浏览器中渲染日期和时间。Flask-Monment是一个Flask程序扩展。能把moment.js集成到Jinja2模板中。
Flask-Moment可以通过pip安装:
pip install flask-moment
from flask.ext.moment import Moment
moment = Moment(app)
{% block scripts %}
{{ super() }}
{{ moment.include_moment() }}
{% endblcok %}
from datetime improt datetime
@app.route('/')
def index():
return render_template('index.html',current_time=datetime.utcnow())
<p>The local date and time is {{ moment(current_time).format('LLL) }}</p>
<p>That was {{ moment(current_time).fromNow(refresh=True) }}</p>
更多moment.js用法:http://momentjs.com/docs/#/displaying/
Flask-Moment假定服务器端程序处理的时间是“纯正的”datetime对象,且使用UTC表示。
对于一些重复操作(生成表单的HTML代码和验证提交的表单数据),Flask-WTF扩展可以把处理Web表单的过程变成一种愉悦的体验。这个扩展对独立的WTForms包进行了包装,方便集成到Flask程序中。
WTForms官网:http://wtforms.simplecodes.com
pip install flaks-wtf
默认情况,Flask-WTF能保护所有表单面授跨站请求伪造(CSRF)的***。为了实现CSRF保护,Flask-WTF需要程序设置一个密钥。Flask-WTF使用这个密钥生成加密令牌,再用令牌验证请求中表单数据的真伪。
示例代码(设置Flask-WTF):
app = Flask(__name__)
app.config['SECRET_KEY'] = 'hard to guess string'
使用Flask-WTF时,每个Web表单都由一个继承自Form的类表示。
示例代码(一个简单的Web表单,包含一个文本字段和一个提交按钮):
from flaks.ext.wtf import Form
from wtforms import StringField,SubmitField
from wtforms.validators import Required
class NameForm(Form):
name = StringField("What's your name?",validators=[Required()])
submit = SubmitField('Submit')
WTForms支持的HTML标准字段
- StringField 文本字段
- TextAreaField 多行文本字段
- PasswordField 密码文本字段
- HiddenField 隐藏文本字段
- DateField 文本字段,值为 datetime.date 格式
- DateTimeField 文本字段,值为 datetime.datetime 格式
- IntegerField 文本字段,值为整数
- DecimalField 文本字段,值为 decimal.Decimal
- FloatField 文本字段,值为浮点数
- BooleanField 复选框,值为 True 和 False
- RadioField 一组单选框
- SelectField 下拉列表
- SelectMultipleField 下拉列表,可选择多个值
- FileField 文件上传字段
- SubmitField 表单提交按钮
- FormField 把表单作为字段嵌入另一个表单
- FieldList 一组指定类型的字段
WTForms验证函数
- Email 验证电子邮件地址
- EqualTo 比较两个字段的值;常用于要求输入两次密码进行确认的情况
- IPAddress 验证 IPv4 网络地址
- Length 验证输入字符串的长度
- NumberRange 验证输入的值在数字范围内
- Optional 无输入值时跳过其他验证函数
- Required 确保字段中有数据
- Regexp 使用正则表达式验证输入值
- URL 验证 URL
- AnyOf 确保输入值在可选值列表中
- NoneOf 确保输入值不在可选值列表中
示例代码:(使用Flask-WTF和Flask-Bootstrap渲染表单)
{% extends 'base.html' %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Flasky{% endblock %}
{% block page_content %}
<div class="page-header">
<h2>hello,{% if name %}{{ name }}{% else %}Stranger{% endif%}</h2>
</div>
{{ wtf.quick_form(form)}}
{% endblock %}
示例代码:(视图函数index()不仅要渲染表单,还要接受表单中的数据。)
@app.route('/',methods=['GET','POST'])
def index():
name = None
form = NameForm()
if form.validate_on_submit():
name = form.name.data
form.name.data = ''
return render_template('index.html',form=form,name=name)
问题背景:当用户输入名字后提交表单,再点击浏览器的刷新按钮,会看到一个警告,关于要求再次提交表单之前进行确认。之所以会出这种问题,是因为刷新页面时浏览器会重新发送之前已经发送过的最后一个请求。
解决方案:使用重定向作为POST请求的响应,而不是使用常规响应。重定向是一种特殊的响应,响应内容是URL,而不是包含HTML代码的字符串。浏览器收到这种响应,会向重定向的URL发起GET请求,显示页面的内容。
另一问题:如果使用上面的解决方案,程序在处理POST请求时,使用from.name.data获取用户输入的名字,一旦请求结束,数据也就丢失了。所以需要程序将数据存储到用户会话中,在请求之间“记住”数据。用户是一种私有存储,存在每个连接到服务器的客户端中。
示例代码:
from flask import Flask,render_template,session,redirect,url_for
@app.route('/',methods=['GET','POST'])
def index():
form = NameForm()
if form.validate_on_submit():
session['name'] = form.name.data
return redirect(url_for('index'))
return render_template('index.html',form=form,name=session.get('name')))
问题背景:用户提交了有一项错误的登录表单,服务器发回的响应重新渲染了登录表单,并在表单上面显示信息,提示用户名或密码错误。
示例代码:
from flask ipmort Flask,render_template,session,redirect,url_for,flash
@app.route('/',methods=['GET','POST'])
def index():
form = NameForm()
if form.validate_on_submit():
old_name = session.get('name')
if old_name is not None and old_name != form.name.data:
flash("Looks like you have changed your name")
session['name'] = olde_name
return redirect(url_for('index'))
return render_template('index.html',name=session.get('name'),form=form) ```
* 代码详解:代码会将每一次提交的名字与上一次**存储在会话中的名字**进行比较,如果两者不一样则会发给客户端下一个响应中显示一个信息。
* 仅调用flash()函数并不能把消息显示出来,程序使用的模板要渲染这些信息,最好在基础模板中渲染Flash消息,因为这样所有页面都能使用这些消息。Flask把**get_flashed_messages()函数**开放给模板,用来获取并渲染消息。
{% block content %}
<div class="container">
{% for message in get_flashed_messages() %}
<div class="alert alert-warning">
<button type="button" class="close" data-dismiss="alert">
×
</button>
{{ message }}
</div>
{% endfor %}
{% block page_content %}
{% endblock %}
</div>
{% endblock %}
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。