1 Flask WTF
Web应用程序的一个重要方面是为用户提供用户界面。HTML提供了一个标签,用于设计界面。可以适当地使用Form(表单)元素,例如文本输入,单选按钮,选择等。
用户输入的数据以Http请求消息的形式通过GET或POST方法提交给服务器端脚本。
- 服务器端脚本必须从http请求数据重新创建表单元素。因此,实际上,表单元素必须定义两次 一次在HTML中,另一次在服务器端脚本中。
- 使用HTML表单的另一个缺点是很难(如果不是不可能的话)动态呈现表单元素。HTML本身无法验证用户的输入。
在Flask中,为了处理web表单,我们一般使用Flask-WTF扩展,它封装了WTForms,使用Flask-WTF表单扩展,可以帮助进行CSRF验证,帮助我们快速定义表单模板,而且可以帮助我们在视图中验证表的数据。使用Flask-WTF,我们可以在Python脚本中定义表单字段,并使用HTML模板进行渲染。还可以将验证应用于WTF字段。
三方包安装:
pip install flask-WTF
标准表单字段
WTforms包中包含各种表单字段的定义。下面列出了一些标准表单字段:
字段名 | 说明 |
---|---|
StringField | 文本字段,表示 HTML表单元素 |
TextAreaField | 多行文本字段 |
PasswordField | 密码文本字段 |
HiddenField | 隐藏文本字段 |
DateField | 文本字段,值为datetime.date格式 |
DateTimeField | 文本字段,值为datetime.datetime格式 |
IntegerField | 文本字段,值为整数 |
DecimalField | 文本字段,值为decimal.Decimal |
FloatField | 文本字段,值为浮点数 |
BooleanField | 复选框,值为True和False。表示 HTML表单元素 |
RadioField | 一组单选框 |
SelectField | 下拉列表 |
SelectMultipleField | 下拉列表,可选择多个值 |
FileField | 文本上传字段 |
SubmitField | 表单提交按钮 ,表示表单元素 |
FormField | 把表单作为字段嵌入另一个表单 |
FieldList | 一组指定类型的字段 |
WTForms常用验证函数
验证函数 | 说明 |
---|---|
DataRequired | 确保字段中有数据 |
检查字段中的文本是否遵循电子邮件ID约定 | |
IPAddress | 在输入字段中验证IP地址 |
EqualTo | 比较两个字段的值,常用于比较两次密码输入 |
Length | 验证输入的字符串长度 |
NumberRange | 验证输入的值在数字范围内 |
URL | 验证URL |
AnyOf | 验证输入值在可选列表中 |
NoneOf | 验证输入值不在可选列表中 |
注意:
使用Flask-WTF需要配置参数SECRET_KEY。
CSRF_ENABLED是为了CSRF(跨站请求伪造)保护。 SECRET_KEY用来生成加密令牌,当CSRF激活的时候,该设置会根据设置的密匙生成加密令牌。
用法示例:
from flask_wtf import FlaskForm
from wtforms import StringField, TextAreaField, SubmitField, RadioField, IntegerField, SelectField
from wtforms import validators, ValidationError
from flask import Flask, render_template, request, flash
app = Flask(__name__)
app.secret_key = 'development key'
#定义表单模型
class ContactForm(FlaskForm):
# label: 名字 validators:验证器/验证器
name = StringField(label="名字", validators=[validators.DataRequired("输入名字")])
gender = RadioField(label='性别', choices=[('M', 'Male'), ('F', 'Female')])
address = TextAreaField(label="地址")
email = StringField(label="邮箱", validators=[validators.DataRequired("输入邮件地址"),
validators.Email("输入邮件地址,必须满足格式**@**")])
age = IntegerField(label="年龄")
language = SelectField(label='语言', choices=[('cpp', 'C++'),
('py', 'Python')])
submit = SubmitField(label="提交")
@app.route("/contact", methods=["POST", "GET"])
def contact():
form = ContactForm()
if request.method == "POST":
# 判断form中的数据是否合理
# 如果form中的数据完全满足所有的验证器,则返回真,否则返回假
if not form.validate_on_submit():
name = form.name.data
email = form.email.data
gender = form.gender.data
address = form.address.data
print("数据错误")
print(name, email, gender, address)
flash('填入数据')
return render_template('contact.html', form=form)
else:
name = form.name.data
email = form.email.data
print(name, email)
return render_template("success.html")
elif request.method == 'GET':
return render_template('contact.html', form=form)
if __name__ == '__main__':
app.run(debug=True)
contact.html内容:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
{% for message in form.name.errors %}
<div>{{ message }}</div>
{% endfor %}
{% for message in form.email.errors %}
<div>{{ message }}</div>
{% endfor %}
<form action = "http://localhost:5000/contact" method="POST">
<div style = font-size:20px; font-weight:bold; margin-left:150px;>
{{ form.name.label }}<br>
{{ form.name }}
<br>
{{ form.gender.label }}
{{ form.gender }} <br>
{{ form.address.label }}
{{ form.address }}
<br>
{{ form.email.label }}<br>
{{ form.email }}
<br>
{{ form.age.label }}<br>
{{ form.age }}
<br>
{{ form.language.label }}<br>
{{ form.language }}
<br>
{{ form.csrf_token }}
{{ form.submit }}
</div>
</form>
</body>
</html>
success.html内容:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
Form posted success!!!
</body>
</html>
浏览器输入URL:http://localhost:5000/contact
点击提交
2 Flask SQLAlchemy
Flask本身不限定数据库的选择,你可以选择SQL或NOSQL的任何一种。也可以选择更方便的SQLALchemy,类似于Django的ORM。SQLALchemy实际上是对数据库的抽象,让开发者不用直接和SQL语句打交道,而是通过Python对象来操作数据库,在舍弃一些性能开销的同时,换来的是开发效率的较大提升。
SQLAlchemy是一个关系型数据库框架,它提供了高层的ORM和底层的原生数据库的操作。flask-sqlalchemy是一个简化了SQLAlchemy操作的flask扩展。
常用的SQLAlchemy字段类型
类型 | python类型 | 说明 |
---|---|---|
Integer | int | 普通整数,一般是32位 |
SmallInteger | int | 取值范围小的整数,一般是16位 |
BigInteger | int或long | 不限制精度的整数 |
Float | float | 浮点数 |
Numeric | decimal.Decimal | 普通整数,一般是32位 |
String | str | 变长字符串 |
Text | str | 变长字符串,对较长或不限长度的字符串做了优化 |
Unicode | unicode | 变长Unicode字符串 |
UnicodeText | unicode | 变长Unicode字符串,对较长或不限长度的字符串做了优化 |
Boolean | bool | 布尔值 |
Date | datetime.date | 时间 |
Time | datetime.datetime | 日期和时间 |
LargeBinary | str | 二进制文件 |
常用的SQLAlchemy查询过滤器
过滤器 | 说明 |
---|---|
filter() | 把过滤器添加到原查询上,返回一个新查询 |
filter_by() | 把等值过滤器添加到原查询上,返回一个新查询 |
limit | 使用指定的值限定原查询返回的结果 |
offset() | 偏移原查询返回的结果,返回一个新查询 |
order_by() | 根据指定条件对原查询结果进行排序,返回一个新查询 |
group_by() | 根据指定条件对原查询结果进行分组,返回一个新查询 |
常用的SQLAlchemy查询执行器
方法 | 说明 |
---|---|
all() | 以列表形式返回查询的所有结果 |
first() | 返回查询的第一个结果,如果未查到,返回None |
first_or_404() | 返回查询的第一个结果,如果未查到,返回404 |
get() | 返回指定主键对应的行,如不存在,返回None |
get_or_404() | 返回指定主键对应的行,如不存在,返回404 |
count() | 返回查询结果的数量 |
paginate() | 返回一个Paginate对象,它包含指定范围内的结果 |
SQLAlchemy常用操作
SQLAlchemy的Session对象管理ORM对象的所有持久性操作。
以下session方法执行CRUD操作:
db.session.add (模型对象) - 将记录插入到映射表中
db.session.delete (模型对象) - 从表中删除记录
model.query.all() - 从表中检索所有记录(对应于SELECT查询)
可以通过使用filter属性将过滤器应用于检索到的记录集。例如,要在学生表中检索city ='Hyderabad’的记录:
Students.query.filter_by(city = ’Hyderabad’).all()
程序代码示例:
from flask import Flask, render_template, request, flash, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.secret_key = 'development key'
class Config(object):
# sqlalchemy的配置参数 帐号:密码@地址:端口/数据库
SQLALCHEMY_DATABASE_URI = "mysql+pymysql://root:127.0.0.1:3306/test_db"
# 设置sqlalchemy自动更跟踪数据库(数据库表手动更新是同步跟新到对象)
SQLALCHEMY_TRACK_MODIFICATIONS = True
#查询时会显示原始SQL语句
SQLALCHEMY_ECHO = True
#加载数据库配置对象
app.config.from_object(Config)
# 创建数据库sqlalchemy工具对象
db = SQLAlchemy(app)
class students(db.Model):
id = db.Column('student_id', db.Integer, primary_key=True)
name = db.Column(db.String(100))
city = db.Column(db.String(50))
addr = db.Column(db.String(200))
pin = db.Column(db.String(10))
def __init__(self, name, city, addr, pin):
self.name = name
self.city = city
self.addr = addr
self.pin = pin
@app.route('/')
def show_all():
return render_template('show_all.html', students=students.query.all())
@app.route('/new', methods=['GET', 'POST'])
def new():
if request.method == 'POST':
if not request.form['name'] or not request.form['city'] or not request.form['addr']:
flash('Please enter all the fields', 'error')
else:
student = students(request.form['name'], request.form['city'],
request.form['addr'], request.form['pin'])
db.session.add(student)
db.session.commit()
flash('Record was successfully added')
return redirect(url_for('show_all'))
return render_template('new.html')
if __name__ == '__main__':
db.create_all()
app.run(debug=True)
new.html代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<form action="{{ request.path }}" method="post">
<label for="name">Name</label><br>
<input type = "text" name = "name" placeholder = "Name" /><br>
<label for = "city">City</label><br>
<input type = "text" name = "city" placeholder = "city" /><br>
<label for = "addr">addr</label><br>
<textarea name = "addr" placeholder = "addr"></textarea><br>
<label for = "PIN">pin</label><br>
<input type = "text" name = "pin" placeholder = "pin" /><br>
<input type = "submit" value = "Submit" />
</form>
</body>
</html>
show_all.html代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<h3>
<a href = "{{ url_for('show_all') }}">Comments - Flask
SQLAlchemy example</a>
</h3>
<hr/>
{%- for message in get_flashed_messages() %}
{{ message }}
{%- endfor %}
<h3>Students (<a href = "{{ url_for('new') }}">Add Student
</a>)
</h3>
<table>
<thead>
<tr>
<th>Name</th>
<th>City</th>
<th>Address</th>
<th>Pin</th>
</tr>
</thead>
<tbody>
{% for student in students %}
<tr>
<td>{{ student.name }}</td>
<td>{{ student.city }}</td>
<td>{{ student.addr }}</td>
<td>{{ student.pin }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>