Python_Flask系列_3.视图,模板
前言
一、视图
第一个Flask程序
from flask import Flask
app = Flask(__name__)#Flask类接收一个参数__name__
@app.route('/')# 装饰器的作用是将路由映射到视图函数index
def hello_world():
return 'Hello World'
if __name__ == '__main__':# Flask应用程序实例的run方法启动WEB服务器
app.run(debug=True)
路由系统
● @app.route('/user/
● @app.route('/post/int:post_id')
● @app.route('/post/float:post_id')
● @app.route('/post/path:path')
● @app.route('/login', methods=['GET', 'POST'])
# 路由传递的参数默认当做string处理,这里指定int,尖括号中冒号后面的内容是动态的
@app.route('/user/<int:id>')
def test(id):
abort(404)#如果abort函数被触发,其后面的语句将不会执行
return 'test %d' %id,999#return后面可以自主定义状态码(即使这个状态码不存在)。
#重定向redirect
@app.route('/',methods=['GET', 'POST'])
def test1():
#return redirect('http://www.baidu.com')
return redirect(url_for('test',_external=True))#url_for()接收视图函数名作为参数,返回对应的URL;
#通过装饰器来实现捕获异常
@app.errorhandler(404)
def error(e):
return f'您请求的页面不存在了,请确认后再次访问!{e}'
#@app.errorhandler(404)
#def page_not_found(e):
# return render_template('404.html'), 404
@app.route('/')
def hello_itcast():
return render_template('index.html')
@app.route('/user/<name>')
def hello_user(name):
return render_template('index.html', name=name)
二、模板
- Jinja2
Jinja2:是 Python 下一个被广泛应用的模板引擎,是由Python实现的模板语言,他的设计思想来源于 Django 的模板引擎,并扩展了其语法和一系列强大的功能,其是Flask内置的模板语言。
1.模板的基本语法
渲染模版函数
● Flask提供的 render_template 函数封装了该模板引擎
● render_template 函数的第一个参数是模板的文件名,后面的参数都是键值对,表示模板中变量对应的真实值。
{% if user %}
<title> hello {{user}} </title>
{% else %}
<title> welcome to flask </title>
{% endif %}
#--------------------------------
<ul>
{% for index in indexs %}
<li> {{ index }} </li>
{% endfor %}
</ul>
#--------------------------------
#变量
<p>{{mydict['key']}}</p>
<p>{{mylist[1]}}</p>
<p>{{mylist[myvariable]}}</p>
#过滤器
#字符串操作
<p>{{ '<em>hello</em>' | safe }}</p>#禁用转义;
<p>{{ 'hello' | capitalize }}</p>#把变量值的首字母转成大写,其余字母转小写;
<p>{{ 'HELLO' | lower }}</p>#值转成小写
<p>{{ 'hello' | upper }}</p>#值转成大写
<p>{{ 'hello' | title }}</p>#值中的每个单词的首字母都转成大写
<p>{{ ' hello world ' | trim }}</p>#值的首尾空格去掉
<p>{{ 'olleh' | reverse }}</p>#字符串反转
<p>{{ '%s is %d' | format('name',17) }}</p>#格式化输出
<p>{{ '<em>hello</em>' | striptags }}</p>#渲染之前把值中所有的HTML标签都删掉
#列表操作
<p>{{ [1,2,3,4,5,6] | first }}</p>
<p>{{ [1,2,3,4,5,6] | last }}</p>
<p>{{ [1,2,3,4,5,6] | length }}</p>
<p>{{ [1,2,3,4,5,6] | sum }}</p>
<p>{{ [6,2,3,1,5,4] | sort }}</p>
#自定义路由过滤器
#实现方式一:通过调用应用程序实例的add_template_filter方法实现自定义过滤器。该方法第一个参数是函数名,第二个参数是自定义的过滤器名称。
def filter_double_sort(ls):
return ls[::2]
app.add_template_filter(filter_double_sort,'double_2')
#实现方式二:用装饰器来实现自定义过滤器。装饰器传入的参数是自定义的过滤器名称。
@app.template_filter('db3')
def filter_double_sort(ls):
return ls[::-3]
#url_for 方法:
{{ request.url }}
{{ config.SQLALCHEMY_DATABASE_URI }}
{{ url_for('index') }}#函数名+参数
{{ url_for('login', id=1) }}
#get_flashed_messages方法:#返回之前在Flask中通过 flash() 传入的信息列表。
{% for message in get_flashed_messages() %}
{{ message }}
{% endfor %}
2.宏、继承、包含
宏、继承、包含:
宏(Macro)、继承(Block)、包含(include)均能实现代码的复用。
-
继承(Block)的本质是代码替换,一般用来实现多个页面中重复不变的区域。
-
宏(Macro)的功能类似函数,可以传入参数,需要定义、调用
-
包含(include)是直接将目标模板文件整个渲染出来
-
1.宏,类似于python中的函数,宏的作用就是在模板中重复利用代码,避免代码冗余。
#定义带参数的宏
{% macro input(name,value='',type='text',size=20) %}
<input type="{{ type }}"
name="{{ name }}"
value="{{ value }}"
size="{{ size }}"/>
{% endmacro %}
#调用宏,并传递参数
{{ input(value='name',type='password',size=40)}}
#把宏单独抽取出来,封装成html文件,其它模板中导入使用
#macro.html
{% macro function() %}
<input type="text" name="username" placeholde="Username">
<input type="password" name="password" placeholde="Password">
<input type="submit">
{% endmacro %}
#其它模板文件中先导入,再调用
{% import 'macro.html' as func %}
{% func.function() %}
- 2.模板继承,模板继承是为了重用模板中的公共内容。
#父模板:base.html
{% block top %}
顶部菜单
{% endblock top %}
{% block content %}
{% endblock content %}
{% block bottom %}
底部
{% endblock bottom %}
#子模板
{% extends 'base.html' %}
{% block content %}
需要填充的内容
{% endblock content %}
- 3.包含(Include),它的功能是将另一个模板整个加载到当前模板中,并直接渲染。
{\% include 'hello.html' ignore missing %}
3.请求和响应Request
- request常用的属性
属性 | 说明 |
---|---|
data | 记录请求的数据 |
form | 记录请求中的表单数据 |
args | 记录请求中的查询参数 |
cookies | 请求中的cookie字典对象 |
headers | 记录请求中的报文头 |
method | 记录请求使用的HTTP方法 |
url | 记录请求的URL地址 |
from flask import Flask
from flask import request
from flask import render_template
from flask import redirect
from flask import make_response
app = Flask(__name__)
@app.route('/login.html', methods=['GET', "POST"])
def login():
# obj = request.files['the_file_name']
# obj.save('/var/www/uploads/' + secure_filename(f.filename))
# 响应相关信息
# return "字符串"
# return render_template('html模板路径',**{})
# return redirect('/index.html')
# response = make_response(render_template('index.html'))
# response是flask.wrappers.Response类型
# response.delete_cookie('key')
# response.set_cookie('key', 'value')
# response.headers['X-Something'] = 'A value'
# return response
return "内容"
if __name__ == '__main__':
app.run()
三、特殊模块
1.cookie和session(会话)
cookie
#设置cookie
@app.route('/cookie')
def set_cookie():
resp = make_response('内容')
resp.set_cookie('username', 'zy',max_age=3600)
return resp
#获取cookie
@app.route('/request')
def resp_cookie():
resp = request.cookies.get('username')# 返回的是一个字典,可以获取字典里的相应的值
return resp
# 删除cookie
@app.route('/request')
def delete_cookie():
resp = make_response('删除成功')# 返回的是一个字典,可以获取字典里的相应的值
resp.delete_cookie('username')
return resp
session(会话)
#Session对象也是一个字典对象,包含会话变量和关联值的键值对。
session['username'] = 'xxx'#设置:
session.pop('username', None)#删除
2.上下文:相当于一个容器,保存了Flask程序运行过程中的一些信息。
Flask中有两种上下文,请求上下文和应用上下文。
请求上下文(request context)
request和session都属于请求上下文对象。
● request:封装了HTTP请求的内容,针对的是http请求。举例:user = request.args.get('user'),获取的是get请求的参数。
● session:用来记录请求会话中的信息,针对的是用户信息。举例:session['name'] = user.id,可以记录用户信息。还可以通过session.get('name')获取用户信息。
3.请求钩子
请求钩子是通过装饰器的形式实现,Flask支持如下四种请求钩子:
● before_first_request:在处理第一个请求前运行。
● before_request:在每次请求前运行。
● after_request:如果没有未处理的异常抛出,在每次请求后运行。
● teardown_request:在每次请求后运行,即使有未处理的异常抛出。
4.Flask-Script扩展命令行
比如我们可以通过python hello.py runserver --host ip地址,告诉服务器在哪个网络接口监听来自客户端的连接。默认情况下,服务器只监听来自服务器所在计算机发起的连接,即localhost连接。
我们可以通过python hello.py runserver --help来查看参数。
from flask import Flask
from flask_script import Manager
app = Flask(__name__)
manager = Manager(app)
@app.route('/')
def index():
return '床前明月光'
if __name__ == "__main__":
manager.run()
5.WTForms
为了处理web表单,我们一般使用Flask-WTF扩展,它封装了WTForms,并且它有验证表单数据的功能。
- WTForms支持的HTML标准字段
字段对象 | 说明 |
---|---|
StringField | 文本字段 |
TextAreaField | 多行文本字段 |
PasswordField | 密码文本字段 |
HiddenField | 隐藏文本字段 |
DateField | 文本字段,值为datetime.date格式 |
DateTimeField | 文本字段,值为datetime.datetime格式 |
IntegerField | 文本字段,值为整数 |
DecimalField | 文本字段,值为decimal.Decimal |
FloatField | 文本字段,值为浮点数 |
BooleanField | 复选框,值为True和False |
RadioField | 一组单选框 |
SelectField | 下拉列表 |
SelectMultipleFeld | 下拉列表,可选择多个值 |
FileField | 文本上传字段 |
SubmitField | 表单提交按钮 |
FormField | 把表单作为字段嵌入另一个表单 |
FieldList | 一组指定类型的字段 |
- WTForms常用验证函数
验证函数 | 说明 |
---|---|
DataRequired | 确保字段中有数据 |
EqualTo | 比较两个字段的值,常用于比较两次密码输入 |
Length | 验证输入的字符串长度 |
NumberRange | 验证输入的值在数字范围内 |
URL | 验证URL |
AnyOf | 验证输入值在可选列表中 |
NoneOf | 验证输入值不在可选列表中 |
from flask import Flask,render_template,redirect,url_for,session,request,flash
from flask_wtf import FlaskForm
from wtforms import SubmitField, StringField, PasswordField, Form
from wtforms.validators import DataRequired,EqualTo
app = Flask(__name__)
app.config['SECRET_KEY']='silents'
#自定义表单类,文本字段、密码字段、提交按钮
class Login(FlaskForm):
us = StringField(label=u'用户:',validators=[DataRequired()])
ps = PasswordField(label=u'密码',validators=[DataRequired(),EqualTo('ps2','err')])
ps2 = PasswordField(label=u'确认密码',validators=[DataRequired()])
submit = SubmitField(u'提交')
@app.route('/login')
def login():
# return render_template('login.html',form=form)
return redirect(url_for('index'))
#定义根路由视图函数,生成表单对象,获取表单数据,进行表单数据验证
@app.route('/',methods=['GET','POST'])
def index():
form = Login()
if form.validate_on_submit():
name = form.us.data
pswd = form.ps.data
pswd2 = form.ps2.data
print (name,pswd,pswd2)
return redirect(url_for('login'))
else:
if request.method=='POST':
flash(u'信息有误,请重新输入!')
print (form.validate_on_submit())
return render_template('login.html',form=form)
if __name__ == '__main__':
app.run(debug=True)
<form method="post">
#设置csrf_token
{{ form.csrf_token() }}
{{ form.us.label }}
<p>{{ form.us }}</p>
{{ form.ps.label }}
<p>{{ form.ps }}</p>
{{ form.ps2.label }}
<p>{{ form.ps2 }}</p>
<p>{{ form.submit() }}</p>
{% for x in get_flashed_messages() %}
{{ x }}
{% endfor %}
</form>
四、例子
1.登录
from flask import Flask, session, redirect, url_for, escape, request
app = Flask(__name__)
@app.route('/')
def index():
if 'username' in session:
return 'Logged in as %s' % escape(session['username'])
return 'You are not logged in'
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
session['username'] = request.form['username']
return redirect(url_for('index'))
return '''
<form action="" method="post">
<p><input type=text name=username>
<p><input type=submit value=Login>
</form>
'''
@app.route('/logout')
def logout():
# remove the username from the session if it's there
session.pop('username', None)
return redirect(url_for('index'))
# set the secret key. keep this really secret:
app.secret_key = 'aaaa'
2.FBV与CBV
#FBV
from flask import Flask,views
app = Flask(__name__)
def wrapper(func):
def inner(*args,**kwargs):
print('beforeFunc')
ret = func(*args,**kwargs)
return ret
return inner
# FBV
@app.route('/index1',methods=['GET','POST'])
@wrapper
def index1():
return "index1"
if __name__ == '__main__':
app.run()
#CBV
#在CBV中,路由信息只能通过add_url_rule()方法添加
from flask import Flask, views
app = Flask(__name__)
class Login(views.MethodView):
methods = ['POST', 'GET']
# 如果需要在CBV中加装饰器的话,括号里就是装饰器的内存地址,可以传多个
decorators = ()
def get(self):
print('get 请求')
return 'login get'
def post(self):
print('post 请求')
return 'login post'
app.add_url_rule('/login', view_func=Login.as_view(name='login'))
if __name__ == "__main__":
app.run(debug=True)
3.restful
flask-restful扩展通过api.add_resource()方法来添加路由,方法的第一个参数是一个类名,该类继承Resource基类,其成员方法定义了不同的HTTP请求方法的逻辑;第二个参数定义了URL路径。在Users类中,我们分别实现了get、post、delete方法,分别对应HTTP的GET、POST、DELETE请求。
另外,flask-restful还提供了argparse,它可以方便地实现对http请求中客户端发送过来的数据进行校验处理,这有点像表单中的验证方法,在实际项目中非常实用。
程序启动以后,我们访问 http://127.0.0.1:5001/users,GET请求时会给出USERS的内容、POST请求时会在USERS中添加一项(如果不存在)并返回USERS更新后的内容。DELETE请求则清空USERS并返回空。
from flask import Flask, jsonify
from flask_restful import Api, Resource, reqparse
USERS = [
{"name": "zhangsan"},
{"name": "lisi"},
{"name": "wangwu"},
{"name": "zhaoliu"}
]
class Users(Resource):
# def get(self):
# return jsonify(USERS)
def get(self, userid):
return jsonify(
{"name": USERS[int(userid)].get("name")}
)
def post(self):
args = reqparse.RequestParser() \
.add_argument('name', type=str, location='json', required=True, help="名字不能为空") \
.parse_args()
if args['name'] not in USERS:
USERS.append({"name": args['name']})
return jsonify(USERS)
def delete(self):
USERS = []
return jsonify(USERS)
app = Flask(__name__)
api = Api(app, default_mediatype="application/json")
api.add_resource(Users, '/users')
app.run(host='0.0.0.0', port=5001, use_reloader=True)
4.登录2
from flask import Flask, request, render_template
app = Flask(__name__) # Flask类接收一个参数__name__
app.config['SECRET_KEY'] = 'silents is gold'
@app.route('/') # 装饰器的作用是将路由映射到视图函数index
def hello_world():
return 'Hello World'
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
print(username, password)
return render_template('login.html', method=request.method)
if __name__ == '__main__': # Flask应用程序实例的run方法启动WEB服务器
app.run(debug=True)
<form method='post'>
<input type="text" name="username" placeholder='Username'>
<input type="password" name="password" placeholder='password'>
<input type="submit">
</form>
总结
不言而喻,重中之重!!