Flask框架及jinja2引擎模版
什么是Flask框架?
Flask是一个使用 Python 编写的轻量级 Web 应用框架。
导入Flask类;
from flask import Flask
#实例化一个Flask对象,
# __name__是模块的名称或者包的名称
# 作用: 根据这个参数确定flask应用的路径, 从而快速查找模板和html文件的默认路径;
# 模块就是python文件; 包就是目录(跟普通目录多加一个__init__.py);
app = Flask(__name__)
#基本路由:通过路由绑定一个视图函数
# @app.route('/'): 告诉Flask哪个URL才能出发对应的函数, 又称为路由;
# 对应定义了一个视图函数, 也就是返回给用户浏览器显示的内容;
@app.route('/')
def index():return '<h1>hello world</h1>'
@app.route('/login/')
def login():return 'login'
# 运行Flask应用, 可以指定ip和端口;
# '0.0.0.0' 所有的IP都可以访问到;
app.run('0.0.0.0',9000)
路由与变量规则
常见路由的规则
- url的一部分可以标记为变量<变量名>
- flask中路由里面的变量常见类型:
int, string, float, path, uuid
from flask import Flask,request
app = Flask(__name__)
@app.route('/<int:id>/coomments/')
def comment1(id):return 'Comment id:%s' %(id)
@app.route('/welcome/<string:user>/')
def welcome(user):return user+'用户欢迎您!'
# 特殊的url地址: http://www.baidu.com/query?id=123&name=cooffee
@app.route('/query')
def query():id = request.args.get('id')name = request.args.get('name')return 'id:%s,name:%s' %(id,name)
app.run()
url构建反向URL
from flask import Flask,request,url_for
app = Flask(__name__)
@app.route('/welcome/<string:user>/')
def welcome(user):return user + "用户欢迎您"
@app.route('/path/')
def path():print(request.headers)# 查看默认url地址的请求方法;# methods is a list of methods this rule should be limited# to (``GET``, ``POST`` etc.). By default a rule# just listens for ``GET``print(request.method)# url_for: 根据函数名, 反向生成url地址print("用户正在访问url地址:%s" %(url_for(endpoint='welcome',user='cooffee')))return "用户正在访问url地址:%s" %(url_for(endpoint='welcome',user='cooffee'))
app.run(port=9001)
常见http方法之post与get
1.常见HTTP请求方法:
GET: 1). 获取信息 2). 提交的信息会展示在url: http://www.floating.org/login?user=cooffee&passwd=cooffee
POST:1). 提交信息, 不在url里面展示, 有利于数据的安全性;
2.默认路由接受的请求方法为get
3.如何指定接收多个HTTP请求方法app.route(’/login/’, methods=[‘GET’, ‘POST’])
4.如何判断用户请求的HTTP方法request.method
5.如何获取用户POST请求提交的表单数据request.form[‘user’]
6.模板渲染(jinja2):
why? python中生成html不易修改与维护, 所以将html的内容独立到templates目录中;
how?
1). 去当前项目目录下寻找templtes是否存在?再去判断templates目录下是否有login.html文件?
2). 读取这个login.html文件的内容, 作为render_template的返回值;
3). 最终返回给用户浏览器;
render_template(‘login.html’)
7.重定向和错误?
redirect(url_for(‘index’))
return render_template(‘login.html’, message=“用户名或者密码错误”)
注意:这需要在工程下建立static文件夹来保存css,font,img,js
建立templates文件夹来保存html页面
而run.py为主程序
from flask import Flask,request,render_template,redirect,url_for,abort
app = Flask(__name__)
@app.route('/')
def index():return render_template('index.html')
@app.route('/login/',methods=['GET','POST'])
def login():if request.method == 'POST':print(request.form)# 1. 如何获取到用户提交的信息呢?user = request.form['user']passwd = request.form['passwd']# 2. 判断用户名和密码是否正确if user == 'root' and passwd == 'cooffee':# 如果登陆成功, 跳转到主页;return redirect(url_for('index'))else:# 如果登陆失败, 重新登陆;return render_template('login.html',message='用户名或者密码错误')else:# 用户是GET请求, 返回登陆的html页面# 1. 读取login.html文件的内容# 2. 将读取的内容返回给用户界面return render_template('login.html')
# 404异常处理: 类似于捕获异常
@app.errorhandler(404)
def not_found(e):return render_template('404.html')
# 抛出异常
@app.route('/user/<int:user_id>/')
def user(user_id):if 0<int(user_id)<=100:return "欢迎访问:%s" %(user_id)else:abort(404)
app.run(port=9002)
什么是jinjia2模板引擎?
python的Web开发中, 业务逻辑(实质就是视图函数的内容)和页面逻辑(html
文件)分开的, 使得代码的可读性增强, 代码容易理解和维护;
模板渲染: 在html文件中,通过动态赋值 , 将重新翻译好的html文件(模板引擎生效) 返回给用户的过程。
其他的模板引擎: Mako, Template, Jinja2
jianja2的常用用法
变量显示
Jinja2变量显示语法: {{ 变量名 }}
for循环:
{% for i in li%}
{% endfor %}
if语句
{% if user == ‘root’%}
{% elif user == ‘hello’ %}
{% else %}
{% endif%}
from flask import Flask,render_template
app = Flask(__name__)
class User(object):def __init__(self,name,passwd):self.name = nameself.passwd = passwddef __str__(self):return "<User:%s>" %(self.name)@app.route('/')
def index1():name = "this is a message"li = [1,2,4,5]d = dict(a=1,b=2)u = User("cooffee",'passwd')return render_template('index1.html',name=name,li = li,d= d,u= u)
app.run(port=9003)
html:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<p>变量:</p>{{name}}
<p>列表:</p>{{li}}
{% for i in li %}
<br>列表元素显示:{{i}}
{% endfor %}
<p>字典:</p> {{d}}
<p>字典:</p> {{d.a}}
<p>字典:</p> {{d['b']}}
<p>对象:</p>{{u}}
<table><tr><th>用户名</th><td>密码</td></tr><tr><td>{{u.name}}</td><td>{{u.passwd}}</td></tr>
</table>
</body>
</html>
过滤器与自定义过滤器
什么是过滤器?
实质上就是一个转换函数/方法
import time
from flask import Flask,render_template
app = Flask(__name__)
class User(object):def __init__(self,name,passwd):self.name =nameself.passwd = passwddef __str__(self):return "<User:%s>" %(self.name)
@app.route('/')
def index2():name = 'this is a message'li = [1,2,4,5]d = dict(a=1,b=2)lidict = [{'count':100,'price':30},{'count':110,'price':20}]u = User('cooffee','passwd')timestamp = time.time()return render_template('index2.html',name=name,li=li,d = d,u = u,lidict = lidict,timestamp = timestamp)
def format_data(value,format="%Y-%m-%d %H:%M:%S"):ttime = time.localtime(value)return time.strftime("%Y-%m-%d %H:%M:%S",ttime)#添加过滤器(函数名,过路器名)
app.add_template_filter(format_data,'format_date')@app.route('/index3')
def index3():return render_template('index3.html',timestamp=time.time())
app.run(port=9004)
index2.html:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
{{ timestamp }}
{{ timestamp | format_date }}
/*常见的字符串过滤器操作:*/
<ul><li>{{ name | upper }}</li><li>{{ name | lower }}</li><li>{{ "hello" | capitalize }}</li><li>{{ " hello world" | capitalize }}</li><li>{{ name | reverse }}</li>
</ul>
/*常见数值操作*/
<ul><!--四舍五入--><li>{{ -12.9623423435 | round }}</li><li>{{ -12.9623423435 | round | abs }}</li></ul>
/*列表常见:*/
<ul><li>{{li}}</li><li>{{li|first}}</li><li>{{li|last}}</li><li>{{li|length}}</li><li>{{li|sum}}</li><li>{{li|sort|reverse}}</li><li>{{li|join(":")}}</li><li>{{["hello","world"]|upper}}</li><li>{{["hello","woRld"]|lower}}</li>
</ul>
/*列表包含字典常用操作:*/
<ul>{% for item in lidict | sort(attribute='price',reverse=false) %}{{item}}{% endfor %}
</ul>
</body>
</html>
index3.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
{{timestamp}}
#使用刚才自定义的fotmat_date过滤器
{{timestamp | format_date}}
</body>
</html>
模板的继承
模板继承语法:1. 如何继承某个模板?{% extends "模板名称" %}2. 如何挖坑和填坑?挖坑:{% block 名称 %}默认值{% endblock %}填坑:{% block 名称 %}{% endblock %}3. 如何调用/继承被替代的模板?挖坑:{% block 名称 %}默认值{% endblock %}填坑:{% block 名称 %}#如何继承挖坑时的默认值?{{ super() }}# 后面写新加的方法.........{% endblock %}
base.html:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>{% block title %}首页{% endblock %} | cooffee</title><link rel="stylesheet" href="../static/css/bootstrap.min.css"><link rel="stylesheet" href="../static/css/main.css"><script src="../static/js/bootstrap.min.js"></script>
</head>
<body><!--导航栏-->
<nav class="navbar navbar-default"><div class="container-fluid"><!-- Brand and toggle get grouped for better mobile display --><div class="navbar-header"><button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"><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="index.html"></a></div><!-- Collect the nav links, forms, and other content for toggling --><div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"><ul class="nav navbar-nav"><li class="active" style="padding-bottom: 15px;"><a href="index.html">首页<span class="sr-only">(current)</span></a></li><li><a href="#">国内</a></li><li><a href="#">数读</a></li><li><a href="#">社会</a></li></ul><ul class="nav navbar-nav navbar-right"><li><a href="/login/">登陆</a></li><li><a href="signup.html">注册</a></li></ul></div><!-- /.navbar-collapse --></div><!-- /.container-fluid -->
</nav>
{% block content %}{% endblock %}
<div class="footer">京ICP备11008151号京公网安备11010802014853</div></body>
</html>
index.html:
{% extends "base.html" %}{% block title %}
{# <!--继承挖坑时填的默认值--> #}
{{super()}}{% endblock %}{% block content %}
<div class="container container-fluid"><div class="row"><!--左侧导航--><div class="col-xs-2"><div class="list-group left-side"><a class="list-group-item left-side-active" href="#">综合</a><a class="list-group-item" href="#">电影</a><a class="list-group-item" href="#">电视剧</a><a class="list-group-item" href="#"> 明星</a><a class="list-group-item" href="#">娱乐</a></div></div><!--中间新闻--><div class="col-xs-7"><div class="media"><div class="media-left"><a href="news.html"><img class="media-object news-png"src="../static/img/index1.jpg"alt="新闻图片"></a></div><div class="media-body"><h4 class="media-heading"><b>2年前他为教育和高圆圆分手,今成这般,高圆圆:我有一句MMP如哽在喉</b></h4><div class="news-info"><img class="news-logo" src="../static/img/u=1271327272,62771227&fm=26&gp=0.jpg"><span>王花花</span>.<span>25K评论</span>.<span>7分钟前</span></div></div></div><div class="media"><div class="media-left"><a href="news.html"><img class="media-object news-png"src="../static/img/2.2.jpg"alt="新闻图片"></a></div><div class="media-body"><h4 class="media-heading"><b>2年前他为教育和高圆圆分手,今成这般,高圆圆:我有一句MMP如哽在喉</b></h4><div class="news-info"><img class="news-logo" src="../static/img/u=1271327272,62771227&fm=26&gp=0.jpg"><span>王花花</span>.<span>25K评论</span>.<span>7分钟前</span></div></div></div><div class="media"><div class="media-left"><a href="news.html"><img class="media-object news-png"src="../static/img/2.jpg"alt="新闻图片"></a></div><div class="media-body"><h4 class="media-heading"><b>2年前他为教育和高圆圆分手,今成这般,高圆圆:我有一句MMP如哽在喉</b></h4><div class="news-info"><img class="news-logo" src="../static/img/u=1271327272,62771227&fm=26&gp=0.jpg"><span>王花花</span>.<span>25K评论</span>.<span>7分钟前</span></div></div></div><div class="media"><div class="media-left"><a href="news.html"><img class="media-object news-png"src="../static/img/2.2.jpg"alt="新闻图片"></a></div><div class="media-body"><h4 class="media-heading"><b>2年前他为教育和高圆圆分手,今成这般,高圆圆:我有一句MMP如哽在喉</b></h4><div class="news-info"><img class="news-logo" src="../static/img/u=1271327272,62771227&fm=26&gp=0.jpg"><span>王花花</span>.<span>25K评论</span>.<span>7分钟前</span></div></div></div><div class="media"><div class="media-left"><a href="news.html"><img class="media-object news-png"src="../static/img/index1.jpg"alt="新闻图片"></a></div><div class="media-body"><h4 class="media-heading"><b>2年前他为教育和高圆圆分手,今成这般,高圆圆:我有一句MMP如哽在喉</b></h4><div class="news-info"><img class="news-logo" src="../static/img/u=1271327272,62771227&fm=26&gp=0.jpg"><span>王花花</span>.<span>25K评论</span>.<span>7分钟前</span></div></div></div><div class="media"><div class="media-left"><a href="news.html"><img class="media-object news-png"src="../static/img/2.2.jpg"alt="新闻图片"></a></div><div class="media-body"><h4 class="media-heading"><b>2年前他为教育和高圆圆分手,今成这般,高圆圆:我有一句MMP如哽在喉</b></h4><div class="news-info"><img class="news-logo" src="../static/img/u=1271327272,62771227&fm=26&gp=0.jpg"><span>王花花</span>.<span>25K评论</span>.<span>7分钟前</span></div></div></div><!--页角--><nav aria-label="Page navigation"><ul class="pagination"><li class="disabled"><a href="#" aria-label="Previous"><span aria-hidden="true">上一页</span></a></li><li class="active"><a href="#">1 <span class="sr-only">(current)</span></a></li><li><a href="#">2 <span class="sr-only">(current)</span></a></li><li><a href="#">3 <span class="sr-only">(current)</span></a></li><li><a href="#">4 <span class="sr-only">(current)</span></a></li><li><a href="#">... <span class="sr-only">(current)</span></a></li><li><a href="#">10 <span class="sr-only">(current)</span></a></li><li class="disabled"><a href="#" aria-label="Previous"><span aria-hidden="true">下一页</span></a></li></ul></nav></div><div class="col-xs-3"><!--搜索栏--><div class="input-group input-info"><input type="text" class="form-control" placeholder="搜一下"></div><!--有害信息--><div class="bad-infomation"><table id="bad-table"><tr><td rowspan="2"><img class="bad-jpg" src="../static/img/4.png"></td><td id="bad-font1">有害信息举报专区</td></tr><tr><td id="bad-font2">举报电话12377</td></tr></table></div><!--新闻推荐--><div class="list-group hot-news"><a href="#" class="list-group-item"><span style="font-size: larger"><b>24小时热闻</b></span></a><a href="#" class="list-group-item"><span style="font-size: large"> Lorem ipsum dolor sit amet,consectetur adipisicing</span><div class="news-info"><span>25K评论</span><span>7分钟前</span></div></a><a href="#" class="list-group-item"><span style="font-size: large"> Lorem ipsum dolor sit amet,consectetur adipisicing</span><div class="news-info"><span>25K评论</span><span>7分钟前</span></div></a><a href="#" class="list-group-item"><span style="font-size: large"> Lorem ipsum dolor sit amet,consectetur adipisicing</span><div class="news-info"><span>25K评论</span><span>7分钟前</span></div></a><a href="#" class="list-group-item"><span style="font-size: large"> Lorem ipsum dolor sit amet,consectetur adipisicing</span><div class="news-info"><span>25K评论</span><span>7分钟前</span></div></a><a href="#" class="list-group-item"><span style="font-size: large"> Lorem ipsum dolor sit amet,consectetur adipisicing</span><div class="news-info"><span>25K评论</span><span>7分钟前</span></div></a></div></div></div>
</div>
{% endblock %}
run.py:
from flask import Flask,request,render_template,redirect,url_for,abort
app = Flask(__name__)
@app.route('/')
def index():return render_template('index.html')
@app.route('/login/',methods=['GET','POST'])
def login():if request.method == 'POST':print(request.form)user = request.form['user']passwd = request.form['passwd']if user == 'root' and passwd == 'cooffee':return redirect(url_for('index'))else:return render_template('login.html',message='用户名或者密码错误')else:return render_template('login.html')
@app.errorhandler(404)
def not_found(e):return render_template('404.html')
@app.route('/user/<int:user_id>/')
def user(user_id):if 0<int(user_id)<=100:return "欢迎访问:%s" %(user_id)else:abort(404)
app.run(port=9002)
宏macro的使用
macro.html:
宏: 是一个函数
{% macro input(type,name,text) %}<div class="form-group"><label>{{text}}</label><input name={{name}} type={{type}} class="form-control username-text"></div>
{% endmacro %}
login.html:
{% extends "base.html" %}{% block title %}
登陆
{% endblock %}
{% block content %}<!--登陆界面--><div class="container container-small login"><h1>登录<small>没有账号?<a href="signup.html">注册</a></small></h1><form action="/login/" method="post"><!--<div class="form-group">--><!--<label>用户名/手机/邮箱</label>--><!--<input name="user" type="text" class="form-control username-text">--><!--</div>--><!--<div class="form-group">--><!--<label>密码</label>--><!--<input name="passwd" type="password" class="form-control username-text">--><!--</div>-->{% import 'macro.html' as macro %}{{macro.input('text','user','用户名/手机/邮箱')}}{{macro.input('password','passwd','密码')}}<div class="form-group"><button class="btn btn-primary btn-block block-sure" type="submit">登录</button></div><div class="form-group"><a href="#">忘记密码?</a></div>{% if message %}<p style="color: red">{{message}}</p>{% endif %}</form></div>
{% endblock %}
session操作
cookie: 客户端浏览器的缓存;
session: 服务端服务器的缓存;
Session 对象存储特定用户会话所需的属性及配置信息。这样,当用户在应用程序的 Web 页之间跳转时,
存储在 Session 对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当用户请求来自应用程序的
Web 页时,如果该用户还没有会话,则 Web 服务器将自动创建一个 Session 对象。当会话过期或被放弃后,
服务器将终止该会话。Session 对象最常见的一个用法就是存储用户的首选项。
from flask import Flask,session
import random
app = Flask(__name__)
# 设置是24位的字符, 每次运行服务器的secret_key都是不同的,
# 服务器重启后会清除上一次存储的session信息值;
app.config['SECRET_KEY'] =random._urandom(24)
# 设置session值;
@app.route("/")
def index():session['name']='cooffee'return "hello world"
# 如何获取?
@app.route('/get/')
def get():return session.get('name')# 如何删除?
@app.route('/delete/')
def delete():print(session.get('name'))session.pop('name')print(session.get('name'))return 'delete'
app.run(port=9006)
判断用户是否登陆的装饰器
import functoolsdef is_login(f):@functools.wraps(f)def wrapper(*args, **kwargs):# run函数代码里面, 如果登陆, session加入user, passwd两个key值;# run函数代码里面, 如果注销, session删除user, passwd两个key值;# 如果没有登陆成功, 则跳转到登陆界面if 'user' not in session:return redirect('/login/')# 如果用户是登陆状态, 则访问哪个路由, 就执行哪个路由对应的视图函数;return f(*args, **kwargs)return wrapper
run.py:
import random
import functools
from flask import Flask, request, render_template, redirect, url_for, abort, sessionapp = Flask(__name__)
app.config['SECRET_KEY'] = random._urandom(24)
def is_login(f):@functools.wraps(f)def wrapper(*args,**kwargs):if 'user' not in session:return redirect(url_for('login'))return f(*args,**kwargs)return wrapper#先判断是否登陆,若登陆才能进入到主页
@app.route('/')
@is_login
def index():return render_template('index.html')@app.route('/login/',methods=['GET','POST'])
def login():if request.method == 'POST':print(request.form)user = request.form['user']passwd = request.form['passwd']if user == 'root' and passwd == 'cooffee':# 将用户名和密码信息存储到session中;session['user']=usersession['passwd']=passwd# 如果登陆成功, 跳转到主页;return redirect(url_for('index'))else:# 如果登陆失败, 重新登陆;return render_template('login.html',message='用户名或者密码错误')else:# 用户是GET请求, 返回登陆的html页面# 1. 读取login.html文件的内容# 2. 将读取的内容返回给用户界面return render_template('login.html')
@app.errorhandler(404)
def not_found(e):return render_template('404.html')
@app.route('/user/<int:user_id>/')
def user(user_id):if 0<int(user_id)<=100:return "欢迎访问:%s" %(user_id)else:abort(404)
app.run(port=9002)
上传文件
实现目标: 如何将文件上传到服务器(保存在指定的文件夹)
import os
from flask import Flask,request,render_template
app = Flask(__name__)
@app.route('/upload/',methods=['GET','POST'])
def upload():if request.method == 'POST':# 获取到用户上传的文件对象f = request.files['faceImg']# 获取当前项目所在目录位置;basepath = os.path.dirname(__file__)# 拼接路径, 存储文件到static/face/xxxxfiename = os.path.join(basepath,'static/face',f.filename)# 保存文件f.save(fiename)return render_template('upload.html',message="上传成功")else:return render_template('upload.html')
app.run(port=9007)
upload.html:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><form action="/upload/" method="post" enctype="multipart/form-data"><input type="file" name="faceImg"><input type="submit" value="提交">{% if message %}<p style="color: red">{{ message }}</p>{% endif %}</form>
</body>
</html>
表单处理flask-wtf
为什么使用Flask-WTF?
request对象公开了所有客户端发送的请求信息。特别是request.form可以访问POST请求提交的表单数据。
尽管Flask的request对象提供的支持足以处理web表单,但依然有许多任务会变得单调且重复。
表单的HTML代码生成和验证提交的表单数据就是两个很好的例子。
优势:
Flask-WTF扩展使得处理web表单能获得更愉快的体验。该扩展是一个封装了与框架无关的WTForms包的Flask集成。什么是表单处理?
在网页中,为了和用户进行信息交互总是不得不出现一些表单。
flask设计了WTForm表单库来使flask可以更加简便地管理操作表单数据。
WTForm中最重要的几个概念如下:1). Form类,开发者自定义的表单必须继承自Form类或者其子类。
Form类最主要的功能是通过其所包含的Field类提供对表单内数据的快捷访问方式。2). 各种Field类,即字段。一般而言每个Field类都对应一个input的HTML标签。
比如WTForm自带的一些Field类比如BooleanField就对应,
SubmitField就对应等等。3). Validator类。这个类用于验证用户输入的数据的合法性。
比如Length验证器可以用于验证输入数据的长度,
FileAllowed验证上传文件的类型等等。另外,flask为了防范csfr(cross-site request forgery)攻击,
默认在使用flask-wtf之前要求app一定要设置过secret_key。
最简单地可以通过app.config[‘SECRET_KEY’] = 'xxxx’来配置。常见的Field类
PasswordField 密码字段,自动将输入转化为小黑点
DateField 文本字段,格式要求为datetime.date一样
IntergerField 文本字段,格式要求是整数
DecimalField 文本字段,格式要求和decimal.Decimal一样
FloatField 文本字段,值是浮点数
BooleanField 复选框,值为True或者False
RadioField 一组单选框
SelectField 下拉列表,需要注意一下的是choices参数确定了下拉选项,
但是和HTML中的 标签一样,其是一个tuple组成的列表,
可以认为每个tuple的第一项是选项的真正的值,而第二项是alias。MultipleSelectField 可选多个值的下拉列表
Validator是验证函数:
Validator是验证函数,把一个字段绑定某个验证函数之后,flask会在接收表单中的数据之前对数据做一个验证,
如果验证成功才会接收数据。验证函数Validator如下,具体的validator可能需要的参数不太一样,这里只给出
一些常用的,更多详细的用法可以参见wtforms/validators.py文件的源码,参看每一个validator类需要哪些参数:*基本上每一个validator都有message参数,指出当输入数据不符合validator要求时显示什么信息。
Email 验证电子邮件地址的合法性,要求正则模式是.+@([.@][^@]+)$
EqualTo 比较两个字段的值,通常用于输入两次密码等场景,可写参数fieldname,不过注意其是一个字符串变量,指向同表单中的另一个字段的字段名
IPAddress 验证IPv4地址,参数默认ipv4=True,ipv6=False。如果想要验证ipv6可以设置这两个参数反过来。
Length 验证输入的字符串的长度,可以有min,max两个参数指出要设置的长度下限和上限,注意参数类型是字符串,不是INT!!
NumberRange 验证输入数字是否在范围内,可以有min和max两个参数指出数字上限下限,注意参数类型是字符串,不是INT!!然后在这个validator的message参数里可以设置%(min)s和%(max)s两个格式化部分,来告诉前端这个范围到底是多少。其他validator也有这种类似的小技巧,可以参看源码。
Optional 无输入值时跳过同字段的其他验证函数
Required 必填字段
Regexp 用正则表达式验证值,参数regex=‘正则模式’
URL 验证URL,要求正则模式是[a-z]+://(?P<host>[/:]+)(?P:[0-9]+)?(?P /.*)?$
AnyOf 确保值在可选值列表中。参数是values(一个可选值的列表)。特别提下,和SelectField进行配合使用时,不知道为什么SelectField的choices中项的值不能是数字。。否则AnyOf的values参数中即使有相关数字也无法识别出当前选项是合法选项。我怀疑NoneOf可能也是一样的套路。
NoneOf 确保值不在可选值列表中
forms.py:
from flask_wtf import FlaskForm
from flask_wtf.file import FileRequired, FileAllowed
from wtforms import StringField, PasswordField, SubmitField, FileField
from wtforms.validators import DataRequired, Lengthclass LoginForm(FlaskForm):name = StringField(label="用户名/邮箱/手机号",validators=[DataRequired(message=u"请输入用户名"),Length(3, 10, message=u"长度不符"),])passwd = PasswordField(label="密码",validators=[DataRequired(message=u"输入密码"),Length(3, 10,message=u"长度不符")] )file = FileField(label="简历",validators=[FileRequired(),FileAllowed(['pdf', 'txt'], 'pdf 能被接收')])
run.py:
import random
from flask import Flask, redirect, render_template, flash
from _Flask框架.forms import LoginForm
from flask_bootstrap import Bootstrap
app = Flask(__name__)
bootstrap = Bootstrap(app)
app.config['SECRET_KEY'] = random._urandom(24)
@app.route('/success/')
def success():return "success"@app.route('/login/', methods=('GET', 'POST'))
def submit():# 实例化表单对象;form = LoginForm()if form.validate_on_submit():print(form.data)flash(form.name.data+'|'+form.passwd.data)return redirect('/success/')return render_template('wtflogin.html', form=form)
app.run(port=8000)
wtflogin.html:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<form method="POST" action="/login/">{{form.hidden_tag()}}<p>{{ form.name.label }} {{ form.name }}{%for e in form.name.errors%}<span style="color: red">{{e}}</span>{% endfor %}</p><p>{{ form.passwd.label }} {{ form.passwd }}{%for j in form.name.errors%}<span style="color: red">{{j}}</span>{%endfor%}</p><p>{{ form.file }}</p><input type="submit" value="Go">
</form>
</body>
</html>
用flask框架及jinja2引擎优化new项目
目录结构
templates
base.html:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>{% block title %}首页{% endblock %} | cooffee</title><link rel="stylesheet" href="../static/css/bootstrap.min.css"><link rel="stylesheet" href="../static/css/main.css"><script src="../static/js/bootstrap.min.js"></script>
</head>
<body><!--导航栏-->
<nav class="navbar navbar-default"><div class="container-fluid"><!-- Brand and toggle get grouped for better mobile display --><div class="navbar-header"><button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"><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="index.html"></a></div><!-- Collect the nav links, forms, and other content for toggling --><div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"><ul class="nav navbar-nav"><li class="active" style="padding-bottom: 15px;"><a href="index.html">首页<span class="sr-only">(current)</span></a></li><li><a href="#">国内</a></li><li><a href="#">数读</a></li><li><a href="#">社会</a></li><li><a href="/sysinfo/">系统信息</a></li><li><a href="#">登陆用户</a></li></ul><ul class="nav navbar-nav navbar-right">{% if 'user' in session %}<!--<li><a><span class="glyphicon glyphicon-user"></span></a></li>--><li><a href="#">{{session.user}}</a></li><li><a href="/logout/">注销</a></li>{% else %}<li><a href="/login/">登陆</a></li><li><a href="/register/">注册</a></li>{% endif %}</ul></div><!-- /.navbar-collapse --></div><!-- /.container-fluid -->
</nav>
{% block content %}{% endblock %}
<div class="footer">京ICP备11008151号京公网安备11010802014853</div></body>
</html>
index.html:
{% extends "base.html" %}{% block title %}{{super()}}{% endblock %}{% block content %}
<div class="container container-fluid"><div class="row"><!--左侧导航--><div class="col-xs-2"><div class="list-group left-side"><a class="list-group-item left-side-active" href="#">综合</a><a class="list-group-item" href="#">电影</a><a class="list-group-item" href="#">电视剧</a><a class="list-group-item" href="#"> 明星</a><a class="list-group-item" href="#">娱乐</a></div></div><!--中间新闻--><div class="col-xs-7"><div class="media"><div class="media-left"><a href="news.html"><img class="media-object news-png"src="../static/img/index1.jpg"alt="新闻图片"></a></div><div class="media-body"><h4 class="media-heading"><b>2年前他为教育和高圆圆分手,今成这般,高圆圆:我有一句MMP如哽在喉</b></h4><div class="news-info"><img class="news-logo" src="../static/img/u=1271327272,62771227&fm=26&gp=0.jpg"><span>王花花</span>.<span>25K评论</span>.<span>7分钟前</span></div></div></div><div class="media"><div class="media-left"><a href="news.html"><img class="media-object news-png"src="../static/img/2.2.jpg"alt="新闻图片"></a></div><div class="media-body"><h4 class="media-heading"><b>2年前他为教育和高圆圆分手,今成这般,高圆圆:我有一句MMP如哽在喉</b></h4><div class="news-info"><img class="news-logo" src="../static/img/u=1271327272,62771227&fm=26&gp=0.jpg"><span>王花花</span>.<span>25K评论</span>.<span>7分钟前</span></div></div></div><div class="media"><div class="media-left"><a href="news.html"><img class="media-object news-png"src="../static/img/2.jpg"alt="新闻图片"></a></div><div class="media-body"><h4 class="media-heading"><b>2年前他为教育和高圆圆分手,今成这般,高圆圆:我有一句MMP如哽在喉</b></h4><div class="news-info"><img class="news-logo" src="../static/img/u=1271327272,62771227&fm=26&gp=0.jpg"><span>王花花</span>.<span>25K评论</span>.<span>7分钟前</span></div></div></div><div class="media"><div class="media-left"><a href="news.html"><img class="media-object news-png"src="../static/img/2.2.jpg"alt="新闻图片"></a></div><div class="media-body"><h4 class="media-heading"><b>2年前他为教育和高圆圆分手,今成这般,高圆圆:我有一句MMP如哽在喉</b></h4><div class="news-info"><img class="news-logo" src="../static/img/u=1271327272,62771227&fm=26&gp=0.jpg"><span>王花花</span>.<span>25K评论</span>.<span>7分钟前</span></div></div></div><div class="media"><div class="media-left"><a href="news.html"><img class="media-object news-png"src="../static/img/index1.jpg"alt="新闻图片"></a></div><div class="media-body"><h4 class="media-heading"><b>2年前他为教育和高圆圆分手,今成这般,高圆圆:我有一句MMP如哽在喉</b></h4><div class="news-info"><img class="news-logo" src="../static/img/u=1271327272,62771227&fm=26&gp=0.jpg"><span>王花花</span>.<span>25K评论</span>.<span>7分钟前</span></div></div></div><div class="media"><div class="media-left"><a href="news.html"><img class="media-object news-png"src="../static/img/2.2.jpg"alt="新闻图片"></a></div><div class="media-body"><h4 class="media-heading"><b>2年前他为教育和高圆圆分手,今成这般,高圆圆:我有一句MMP如哽在喉</b></h4><div class="news-info"><img class="news-logo" src="../static/img/u=1271327272,62771227&fm=26&gp=0.jpg"><span>王花花</span>.<span>25K评论</span>.<span>7分钟前</span></div></div></div><!--页角--><nav aria-label="Page navigation"><ul class="pagination"><li class="disabled"><a href="#" aria-label="Previous"><span aria-hidden="true">上一页</span></a></li><li class="active"><a href="#">1 <span class="sr-only">(current)</span></a></li><li><a href="#">2 <span class="sr-only">(current)</span></a></li><li><a href="#">3 <span class="sr-only">(current)</span></a></li><li><a href="#">4 <span class="sr-only">(current)</span></a></li><li><a href="#">... <span class="sr-only">(current)</span></a></li><li><a href="#">10 <span class="sr-only">(current)</span></a></li><li class="disabled"><a href="#" aria-label="Previous"><span aria-hidden="true">下一页</span></a></li></ul></nav></div><div class="col-xs-3"><!--搜索栏--><div class="input-group input-info"><input type="text" class="form-control" placeholder="搜一下"></div><!--有害信息--><div class="bad-infomation"><table id="bad-table"><tr><td rowspan="2"><img class="bad-jpg" src="../static/img/4.png"></td><td id="bad-font1">有害信息举报专区</td></tr><tr><td id="bad-font2">举报电话12377</td></tr></table></div><!--新闻推荐--><div class="list-group hot-news"><a href="#" class="list-group-item"><span style="font-size: larger"><b>24小时热闻</b></span></a><a href="#" class="list-group-item"><span style="font-size: large"> Lorem ipsum dolor sit amet,consectetur adipisicing</span><div class="news-info"><span>25K评论</span><span>7分钟前</span></div></a><a href="#" class="list-group-item"><span style="font-size: large"> Lorem ipsum dolor sit amet,consectetur adipisicing</span><div class="news-info"><span>25K评论</span><span>7分钟前</span></div></a><a href="#" class="list-group-item"><span style="font-size: large"> Lorem ipsum dolor sit amet,consectetur adipisicing</span><div class="news-info"><span>25K评论</span><span>7分钟前</span></div></a><a href="#" class="list-group-item"><span style="font-size: large"> Lorem ipsum dolor sit amet,consectetur adipisicing</span><div class="news-info"><span>25K评论</span><span>7分钟前</span></div></a><a href="#" class="list-group-item"><span style="font-size: large"> Lorem ipsum dolor sit amet,consectetur adipisicing</span><div class="news-info"><span>25K评论</span><span>7分钟前</span></div></a></div></div></div>
</div>
{% endblock %}
login.html:
{% extends "base.html" %}{% block title %}
登陆
{% endblock %}
{% block content %}<!--登陆界面--><div class="container container-small login"><h1>登录<small>没有账号?<a href="/register/">注册</a></small></h1><form action="/login/" method="post"><!--<div class="form-group">--><!--<label>用户名/手机/邮箱</label>--><!--<input name="user" type="text" class="form-control username-text">--><!--</div>--><!--<div class="form-group">--><!--<label>密码</label>--><!--<input name="passwd" type="password" class="form-control username-text">--><!--</div>-->{% import 'macro.html' as macro %}{{macro.input('text','user','用户名/手机/邮箱')}}{{macro.input('password','passwd','密码')}}<div class="form-group"><button class="btn btn-primary btn-block block-sure" type="submit">登录</button></div><div class="form-group"><a href="#">忘记密码?</a></div>{% if message %}<p style="color: red">{{message}}</p>{% endif %}</form></div>
{% endblock %}
singnup.html:
{% extends "base.html" %}{% block title %}
注册
{% endblock %}
{% block content %}<!--注册界面-->
<div class="container container-small login"><h1>注册<small>没有账号?<a href="/register/">注册</a></small></h1><form action="/register/" method="post">{% import 'macro.html' as macro %}{{macro.input('text','user','用户名/手机/邮箱')}}{{macro.input('password','passwd','密码')}}<!--<div class="form-group">--><!--<label>手机</label>--><!--<input type="text" class="form-control username-text">--><!--</div>--><!--<!–<div class="form-group username-text">–>--><!--<!–<label>验证码</label>–>--><!--<!–<!–<div class="input-group">–>–>--><!--<!–<!–<input type="text" class="form-control">–>–>--><!--<!–<!–<div class="input-group-btn">–>–>--><!--<!–<!–<div class="btn btn-default">获取验证码</div>–>–>--><!--<!–<!–</div>–>–>--><!--<!–<!–</div>–>–>--><!--<!–</div>–>--><!--<div class="form-group">--><!--<label>密码</label>--><!--<input type="password" class="form-control username-text">--><!--</div>--><div class="form-group"><button class="btn btn-primary btn-block block-sure" type="submit">注册</button></div><div class="form-group">注册咖啡或浮云即代表您同意<a href="#">咖啡或浮云服务条款</a></div>{% if message %}<p style="color: red">{{message}}</p>{% endif %}</form></div>
{% endblock %}
macro.html:
{% macro input(type,name,text) %}<div class="form-group"><label>{{text}}</label><input name={{name}} type={{type}} class="form-control username-text"></div>
{% endmacro %}
sysinfo.html:
{% extends 'base.html' %}{% block title %} 系统信息 {% endblock %}{% block content %}{# 表格内容----bootstrap #}
<table class="table table-striped" style="width: 50%; margin: auto " ><tr><td><b style="font-size: large">系统信息</b></td><td></td></tr><tr><td>主机名</td><td>{{ hostname }}</td></tr><tr><td>内核名称</td><td>{{ sysname }}</td></tr><tr><td>发行版本号</td><td>{{ release }}</td></tr><tr><td>内核版本</td><td>{{ version}}</td></tr><tr><td>系统构架</td><td>{{ machine }}</td></tr><tr><td>现在时间</td><td>{{ now_time }}</td></tr><tr><td>开机时间</td><td>{{ boot_time }}</td></tr><tr><td>运行时间</td><td>{{ delta_time }}</td></tr>
</table>{% endblock %}
404.html:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<h2 style="color: red;">404: 页面未找到</h2></body>
</html>
程序
models.py:
import pymysql
conn = pymysql.connect(host='172.25.254.78',user='cooffee',password='cooffee',charset='utf8',db='cooffee')cur = conn.cursor()
def isUserExist(username):sqli = "select * from flaskdata where name='%s'" %(username)res = cur.execute(sqli)if res == 0:return Falseelse:return True
def isPasswdOk(username,passwd):sqli = sqli = "select * from flaskdata where name='%s' and passwd='%s'" %(username, passwd)res = cur.execute(sqli)if res == 0:return Falseelse:return Truedef addUser(username,passwd):sqli = "insert into flaskdata (name, passwd) values('%s', '%s')" %(username, passwd)try:res= cur.execute(sqli)conn.commit()except Exception as e:conn.rollback()return e
if __name__ == "__main__":addUser('root','root')print(isUserExist('root'))print(isPasswdOk('root','root'))
run.py:
import random
import functoolsimport os
from datetime import datetimeimport psutil as psutil
from flask import Flask, request, render_template, redirect, url_for, abort, session
from models import isPasswdOk, isUserExist, addUser
app = Flask(__name__)
app.config['SECRET_KEY'] = random._urandom(24)
def is_login(f):"""判断用户是否登陆的装饰器"""@functools.wraps(f)def wrapper(*args,**kwargs):# run函数代码里面, 如果登陆, session加入user, passwd两个key值;# run函数代码里面, 如果注销, session删除user, passwd两个key值;# 如果没有登陆成功, 则跳转到登陆界面if 'user' not in session:return redirect(url_for('login'))# 如果用户是登陆状态, 则访问哪个路由, 就执行哪个路由对应的视图函数;return f(*args,**kwargs)return wrapper# 用户主页
@app.route('/')
def index():return render_template('index.html')# 用户登陆按钮
@app.route('/login/',methods=['GET','POST'])
def login():if request.method == 'POST':print(request.form)user = request.form['user']passwd = request.form['passwd']if isPasswdOk(user,passwd):# 将用户名和密码信息存储到session中;session['user']=usersession['passwd']=passwdreturn redirect(url_for('index'))else:return render_template('login.html',message='用户名或者密码错误')else:return render_template('login.html')
# 用户注销
@app.route('/logout/')
def logout():session.pop('user',None)session.pop('passwd',None)return redirect(url_for('index'))
# 用户注册
@app.route('/register/',methods=['GET','POST'])
def register():if request.method == 'POST':user = request.form['user']passwd = request.form['passwd']if isUserExist(user):message = "用户已经存在"return render_template('signup.html',message=message)else:addUser(user,passwd)return redirect(url_for('login'))else:return render_template('signup.html')@app.route('/sysinfo/')
@is_login
def sysinfo():info = os.uname()boot_time = psutil.boot_time()boot_time = datetime.fromtimestamp(boot_time)now_time = datetime.now()delta_time = now_time - boot_timedelta_time = str(delta_time).split('.')[0]return render_template('sysinfo.html',hostname=info.nodename,sysname=info.sysname,release=info.release,version= info.version,machine=info.machine,now_time=str(now_time).split('.')[0],boot_time=boot_time,delta_time=delta_time)
# 404异常处理: 类似于捕获异常
@app.errorhandler(404)
def not_found(e):return render_template('404.html')app.run(port=9002)
Flask框架及jinja2引擎模版相关推荐
- Flask框架基础Jinja2模板
Flask框架基础Jinja2模板-- 潘登同学的flask学习笔记 文章目录 Flask框架基础Jinja2模板-- 潘登同学的flask学习笔记 return 模板 Template 模板的使用 ...
- Flask 框架下 Jinja2 模板引擎高层 API 类——Environment
Environment 类版本: 本文所描述的 Environment 类对应于 Jinja2-2.7 版本. Environment 类功能: Environment 是 Jinja2 中的一个核心 ...
- Flask框架二 Jinja2
1.简介: 什么是Jinja2,Jinja2起什么作用 Jinja2是Python下一个被广泛应用的模版引擎,且它自带一个感觉挺nb的转义功能 作用1.它起了让前端和后端分离的作用 2.减少了Flas ...
- Flask框架的学习——04—(模版继承、静态文件的配置、豆瓣APP界面案例实现)
1.模版继承 Flask中的模板可以继承,通过继承可以把模板中许多重复出现的元素抽取出来,放在父模板中,并且父模板通过定义block给子模板开一个口,子模板根据需要,再实现这个block,假设现在有一 ...
- Flask框架10(Jinja2模板继承)
Jinja2模板继承 #!/usr/bin/env python # -*- coding: utf-8 -*- from flask import Flask,render_template &qu ...
- flask框架中的Jinja2模板引擎
简介 在flask框架中通常使用Jinja2模板引擎来实现复杂页面的渲染. 本章主要介绍Jinja2模板引擎的基本结构和使用方法. 如何使用flask框架渲染模板 在模板中传递一个或者多个参数 if语 ...
- Flask 中的Jinja2模板引擎
Flask 中的Jinja2模板引擎 在 Web 项目中,前端的显示效果是通过 HTML 语言来实现的,后端的视图函数将数据或模板文件返回给前端. 前端接收到后端返回的结果后,需要通过模板引擎来渲染页 ...
- Flask框架之Jinja 2模板引擎
文章目录 3.6宏的定义及使用 3.6.1 宏的定义 3.6.2 宏的导入 3.6.3 include的使用 3.7 set和with语句的使用 3.8静态资源文件的加载 3.9模板的继承 3.6宏的 ...
- python之flask框架详解
目录 前言 1.新建文件helloworld.py 2.相关配置参数 3.加载配置文件 3.1配置对象加载 3.2配置文件加载 3.3读取配置 4 路由定义 4.1指定路由地址 4.2 给路由传参 4 ...
最新文章
- uniapp 中如何使用echart_uniapp中如何引用echarts
- Python语言学习之字母R开头函数使用集锦:random/range/replace/reshape用法之详细攻略
- 如何在Windows系统上使用Object Detection API训练自己的数据?
- 《MySQL实战45讲》实践篇 24-29 学习笔记 (主备篇)
- icd植入是大手术吗_骨折手术植入了钢板,骨折痊愈后,需要取出钢板吗?
- 将对话框(提示框)中的内容粘贴到记事本
- ghost不要用作域控的备份
- 吃的苦中苦,方为人上人!
- php修改ip6地址为ip4,CentOS7 设置静态IPv6/IPv4地址
- 命令行模式下编译多个C/C++源文件
- centos7.6 LNMP新版本
- 利用FreeType和OpenGL进行矢量字体渲染
- mysql 运算符转义_我的MYSQL学习心得(五) 运算符
- HG255D刷flash记录
- 【Apple苹果设备刷机】ipad已停用,iTunes无法联系网络等问题
- 参数化三维管网建模系统MagicPipe3D
- SEO优化与网站浏览量提升
- linux远程连接交换机,总结:交换机远程登陆的两种方式,Telnet与SSH那种好?
- 位操作的应用实例(2)位掩码
- 12.2版本数据库ORA-20001: Statistics Advisor: Invalid task name for the current user