目录
一、架构简介
1、什么是Flask?
二、Flask表单操作
三、什么是Flask-SQLAlchemy?
1、如何配置数据库?
2、定义模型
3、模型列类型
4、模型列属性
5、数据查询
6、分页实现
7、Flask-Migrate数据库迁移
1、模型定义
2、一对多关系
3、多对多关系
4、Flask-Migrate数据库迁移
1、为什么使用Flask-Migrate?
2、实现步骤
3、数据库迁移命令行操作 (创建迁移仓库)
5、什么是Flask-Script?
1、为什么使用Flask-Script?
6、Flask常用钩子函数
1、什么是钩子函数?
1、基础概念
2、常用的钩子函数
6、案例
1、路由和变量规则
2、登录验证之http请求方法GET实现案例
3、登录验证值http请求值POST
4、Flask日志记录
一、架构简介
Nginx (engine x) 是一个高性能的 HTTP 和 反向代理 web 服务器,同时也提供了 IMAP/POP3/SMTP 服务 。
uWSGI 是一个 Web 服务器 ,它实现了 WSGI 协议、 uwsgi 、 http 等协议。
Framework 即架构,它是一个语言开发 软件 ,提供了软件开发的框架,使开发更具工程性、简便性和稳定性。
1、什么是Flask?
Flask是一个Web框架,就是提供一个工具,库和技术来允许你构建一个Web应用程序.这个Web应用程序。
可以是一些Web页面,博客, wiki ,基于 Web 的日历应用或商业网站。
1)Flask依赖模块:
web服务网关接口( Python Web Server Gateway Interface, 缩写为 WSGI
Werkzeug 一个 WSGI 工具包, 是为 python 语言定义的 web 服务器和 web 应用程序或框架之间的一
种简单而通用的借口 , 其他语言也有类似的接口)
jinja2 模板引擎
2)Flask 的优势
Flask 属于微框架( micro - framework ) 这一类别 , 微架构通常是很小的不依赖外部库的框架 .
框架很轻量。
更新时依赖小。
专注于安全方面的 bug。
如下,实现一个简单的flask框架:(File:01_flask第一个网站.py)
from flask import Flask
app = Flask(__name__)
#实现首页:http://172.25.254.197:9999/
@app.route('/') #路由
def index(): #视图函数,一定不能重复;
return '这是一个网站的首页'
@app.route('/login/')
def login():
return '正在登陆......'
@app.route('/logout/')
def logout():
return '正在登出......'
if __name__ == '__main__':
#运行flask项目,默认ip和端口是127.0.0.1:5000
#如何特色化指定?host='0.0.0.0' 开放本机的所有IP port=5000 端口必须是整形数
#debug=True:是否开启调试模式,测试环境中一般开启,生产环境一定要关闭。
app.run(host='127.0.0.1',port=9999,debug=True)
运行结果如下:
点击那行网址之后,就进入浏览器了:
二、Flask表单操作
Form 表单,在 Web 应用中无处不在。比如 : 用户登录表单 , 用户注册表单。 所有的表单项都有共性,比如有文字输入框,单选框,密码输入框等;
表单的验证也有共性,比如有非空验证,长度限制,类型验证等。 如果有个框架,能把这些共性抽象出来,那就能大量简化我们的工作。
Python 的 WTForms 就提供了这 些功能,这里我们就要结合Flask 的 WTForms 扩展, Flask - WTF ,来介绍如何在 Web 应用中制作表单。
1、安装和启用
pip install Flask-WTF
2、表单文件
WTForms 让我们在后端代码中定义表单类,并列出表单的字段和相应的验证规则。现在让我们先定义一
个 MyForm 类:
from flask_wtf import Form
from wtforms import StringField
from wtforms.validators import DataRequired
class LoginForm(FlaskForm):
email = StringField(u'邮箱', validators=[
DataRequired(message= u'邮箱不能为空'), Length(1, 64),
Email(message= u'请输入有效的邮箱地址,比如:username@domain.com')])
password = PasswordField(u'密码',
validators=[Required(message= u'密码不能为空')])
submit = SubmitField(u'登录')
3、常见的表单域类型
4、常见验证规则
5、视图函数文件
@app.route('/login/', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
email = form.email.data
...
return render_template('login.html', form=form)
6、验证数据
点击了表单上的提交按钮时, form.validate_on_submit() 判断会做下面两件事情:
通过 is_submitted() 通过判断 HTTP 方法来确认是否提交了表单
通过 WTForms 提供的 validate() 来验证表单数据
当 validate() 验证未通过时,会在表单字段下面显示我们传进去的错误提示(例如 message= u' 邮
箱不能为空 ' )。
7、自定义验证
你可以在表单类创建自定义的验证函数,一个简单的例子:
def validate_username(self, field):
# field.data是用户输入的数据。
if field.data == 'admin':
# ValidationError从wtforms导入,用来向用户显示错误信息,
# 验证函数的名称由validate_fieldname组成。
raise ValidationError(u'超级管理员已被注册,换一个吧。')
也可以在这里对用户数据进行预处理:
# 这个函数对用户输入的网址进行处理(字段名为website)。
def validate_website(self, field):
if field.data[:4] != "http":
field.data = "http://" + field.data
8、获取数据
验证通过后,
使用 form.email.data 来获得数据,
form.email.data
WTForms 提供的静态方法 .data 返回一个以字段名( fifield name )和字段值( fifield value )作为键
值对的字典。
form.data['email']
9、前端文件
form.hidden_tag() 会生成一个隐藏的
标签,其中会渲染任何隐藏的字段,最主要的是 CSRF 字段。
CSRF ( Cross-Site Request Forgery 跨站请求伪造)是一种通过伪装来自受信任用户的请求,来
发送恶意攻击的方法 , WTForms 默认开启 CSRF 保护。
<form method="POST" action="/login/">
{{ form.hidden_tag() }}
{{ form.user.label }}: {{ form.user(size=20) }}
<input type="submit" value="Submit">
</form>
10、Python数据库连接方式
python 中的数据库连接有两种方式 :
Python 标准数据库接口 , 使用 SQL 语句正常操作数据库 , e.g:
MySQL 关系型数据库的 pymsql 模块
用 ORM 来进行数据库连接 , Python 中使用 sqlalchemy , flflask 中典型的 flask_sqlalchemy , 已面向
对象的方式进行数据库的连接与操作
ORM 是什么 ? 有什么优势 ?
ORM ,即 Object - Relational Mapping (对象关系映射),它的作用是在关系型数据库和业务实体对
象之间作一个映射,这样,我们在具体的操作业务对象的时候,就 不需要再去和复杂的 SQL 语句打交
道 ,只需 简单的操作对象的属性和方法 。
三、什么是Flask-SQLAlchemy?
Flask - SQLAlchemy 是一个 Flask 扩展 , 简化了在 Flask 程序中使用 SQLAlchemy 的操作。
SQLAlchemy 是一个很强大的关系型数据库框架 , 支持多种数据库后台。 SQLAlchemy 提
供了高层 ORM , 也提供了使用数据库原生 SQL 的低层功能。
1、如何配置数据库?
1)安装第三方模块
pip install flask-sqlalchemy
2)数据库配置
官方配置信息网址
# app.py文件
from flask_sqlalchemy
import SQLAlchemy
# 获取当前绝对路径 basedir = os.path.abspath(os.path.dirname(__file__))
app = Flask(__name__)
# SQLALCHEMY_DATABASE_URI: 用于连接数据的数据库。
app.config['SQLALCHEMY_DATABASE_URI'] =\
'sqlite:///' + os.path.join(basedir, 'data.sqlite')
# sqlchemy将会追踪对象的修改并且发送信号
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
# 每次请求结束之后都会提交数据库的变动
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
# 数据库与app项目关联, 返回SQLAlchemy实例对象,后面可以使用对象调用数据
db = SQLAlchemy(app)
流行的数据库引擎采用的数据库 URL格式
连接 mysql 数据库报错解决
import pymysql
pymysql.install_as_MySQLdb()
2、定义模型
模型 (Model) 这个术语表示程序使用的持久化实体。
模型列类型
模型列属性
class Role(db.Model):
# __tablename__类变量定义数据库中表的名称。不指定默认为模型名称。
# Flask-SQLAlchemy需要给所有的模型定义主键列,通常命名为id。
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
# repr()显示一个可读字符串,用于调试和测试
def __repr__(self):
return '<Role %r>' % self.name
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), unique=True, index=True)
def __repr__(self):
return '<User %r>' % self.username
3、模型列类型
4、模型列属性
5、数据查询
查询过滤器
查询执行函数
6、分页实现
分页对象拥有的属性
7、Flask-Migrate数据库迁移
分页对象拥有的方法
数据库关系
数据库实体间有三种对应关系:一对一,一对多,多对多。
一对一关系
一个学生对应一个学生档案材料,或者每个人都有唯一的身份证编号。
1、模型定义
# one_to_one.py文件
from app import db
import pymysql
pymysql.install_as_MySQLdb()
class People(db.Model):
__tablename__ = 'peoples'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(20), nullable=False)
"""
关系使用 relationship() 函数表示;
'Card': People类对应的表与Card类对应的表关联;
backref='people': 反向引用, 在Card类中添加新属性people;
uselist=False: 默认为True, 如果为False代表一对一关系;
lazy='select': 决定了 SQLAlchemy 什么时候从数据库中加载数据; 还可以设 定:'joined', 'subquery' , 'dynamic'
可能报错: SAWarning: Multiple rows returned with uselist=False for lazily- loaded
"""
card = db.relationship('Card', backref='people', uselist=False)
def __init__(self, name):
self.name = name
def __repr__(self):
return '<People: %s>' % (self.name)
class Card(db.Model):
__tablename__ = 'cards'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
cardID = db.Column(db.String(30), nullable=False)
# 外键必须用类 sqlalchemy.schema.ForeignKey 来单独声明;
people_id = db.Column(db.Integer, db.ForeignKey('peoples.id'))
def __repr__(self):
return "<Card: %s>" % (self.cardID)
2)基本操作
# one_to_one.py文件
if __name__ == '__main__':
db.drop_all()
db.create_all()
# 增元素
p1 = People(name="西部开源")
p2 = People(name="粉条")
db.session.add_all([p1, p2])
db.session.commit()
card1 = Card(cardID='001', people_id=p1.id)
card2 = Card(cardID='002', people_id=p2.id)
db.session.add_all([card1, card2])
db.session.commit()
# 查找
card_people1 = Card.query.filter_by(cardID='001').first().people people_card1 = People.query.filter_by(id=1).first().card
print(card_people1) print(people_card1)
# 删除
print("删除前: ", Card.query.all(), People.query.all())
card = Card.query.filter_by(cardID='001').first()
db.session.delete(card)
db.session.commit()
print("删除后: ", Card.query.all(), People.query.all())
2、一对多关系
一个学生只属于一个班,但是一个班级有多名学生。
设计数据库表:只需在 学生表 中多添加一个班级号的 ID ;
通过添加主外键约束,避免删除数据时造成数据混乱!
1)模型定义
# one_to_many.py文件
from app import db
import pymysql
pymysql.install_as_MySQLdb()
# 一对多: 外键写在多的一端; Grade:Stdudents== 1:n
class Student(db.Model):
__tablename__ = 'students'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(20), nullable=False)
# 外键
grade_id = db.Column(db.Integer, db.ForeignKey('grades.id'))
def __repr__(self):
return "<Student: %s %s %s>" % (self.id, self.name, self.grade_id)
class Grade(db.Model):
__tablename__ = 'grades'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(20), nullable=False)
# 反向引用, 让Student类中添加属性grade;
students = db.relationship('Student', backref='grade')
def __repr__(self):
return "<Grade: %s %s %s>" % (self.id, self.name, self.students)
2)基本操作
if __name__ == '__main__':
db.drop_all()
db.create_all()
# 增元素
s1 = Student(name="西部开源")
s2 = Student(name="粉条")
db.session.add_all([s1, s2])
db.session.commit()
grade1 =Grade(name='Python开发')
grade1.students.append(s1)
grade1.students.append(s2)
db.session.add_all([grade1])
db.session.commit()
print("添加成功: ", Student.query.all(), Grade.query.all())
# 查找
student_grade1 = Student.query.filter_by(name='粉条').first().grade
grade_students = Grade.query.filter_by(name="Python开发").first().students print(student_grade1)
print(grade_students)
# 删除
print("删除前: ", Student.query.all(), Grade.query.all())
grade1 = Grade.query.filter_by(name='Python开发').first()
db.session.delete(grade1)
db.session.commit()
print("删除后: ", Student.query.all(), Grade.query.all())
3、多对多关系
一个学生可以选择多门课,一门课也有多名学生。
对于多对多表,通过关系表就建立起了两张表的联系!多对多表时建立主外键后,要先删除约束表
内容再删除主表内容。
1)模型定义
# many_to_many.py文件
from app import db
import pymysql
pymysql.install_as_MySQLdb()
# 第三方表
tags=db.Table('tags', db.Column('student_id',db.Integer,db.ForeignKey('math_students.id')),
db.Column('course_id',db.Integer,db.ForeignKey('courses.id')))
class MathStudent(db.Model):
__tablename__ = 'math_students'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(20), nullable=False)
courses = db.relationship('Course', secondary=tags)
def __repr__(self):
return "<MathStudent: %s >" % (self.name)
class Course(db.Model):
__tablename__ = 'courses'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(20), nullable=False)
students = db.relationship('MathStudent', secondary=tags)
def __repr__(self):
return "<Course: %s >" % (self.name)
2)基本操作
# many_to_many.py文件
if __name__ == '__main__':
db.drop_all()
db.create_all()
# 创建
s1 = MathStudent(name="拉格朗日")
s2 = MathStudent(name="麦克劳林")
s3 = MathStudent(name="罗尔")
course1 = Course(name="高等数学")
course2 = Course(name="线性代数")
s1.courses.append(course1)
s1.courses.append(course2)
s2.courses.append(course1)
s3.courses.append(course2)
db.session.add_all([s1, s2, s3, course1, course2])
db.session.commit()
# 删除
courseObj = Course.query.filter_by(name="线性代数").first()
studentObj = MathStudent.query.filter_by(name="罗尔").first()
print("删除前选择线性代数的学生: ", courseObj.students)
courseObj.students.remove(studentObj) print("删除后择线性代数的学生: ",
courseObj.students)
# 其他操作相同
4、Flask-Migrate数据库迁移
1、为什么使用Flask-Migrate?
在我们用 sqlchemy 模块创建完几个表时,如果在实际生产环境中,需要对表结构进行更改,应该怎么
办呢?总不能把表删除了吧,这样数据就会丢失了。
更好的解决办法是使用数据库迁移框架,它可以追踪数据库模式的变化,然后把变动应用到数据库中。
在 Flask 中可以使用 Flask-Migrate 扩展,来实现数据迁移。并且集成到 Flask-Script 中,所有操作通过命
令就能完成。
2、实现步骤
安装 flflask-migrate 和 flflask-script 模块。
pip3 install flask-migrate
pip3 install flask-script
配置代码
manager = Manager(app)
db = SQLAlchemy(app) #第一个参数是Flask的实例,第二个参数是Sqlalchemy数据库实例
migrate = Migrate(app,db)
#manager是Flask-Script的实例,添加一个db命令
manager.add_command('db',MigrateCommand)
3、数据库迁移命令行操作 (创建迁移仓库)
这个命令会创建 migirations 文件夹,所有迁移文件都放在里面
python3 xxx.py db init
1)自动创建迁移脚本
数据库迁移工作由迁移脚本完成。这个脚本有两个函数,分别叫做 upgrade() 和 downgrade() 。
upgrade() 函数实施数据库更改,是迁移的一部分, downgrade() 函数则删除它们。通过添加和删除数
据库变化的能力, Alembic 可以重新配置数据库从历史记录中的任何时间点。
python3 xxx.py db migrate -m "版本名(注释)"
2)更新数据库
一旦迁移脚本被审查且接受,就可以使用 db upgrade 命令更新到数据库中:
python3 xxx.py db upgrade
当我们需要修改表结构时,如何实现数据库迁移呢 ?
直接在 xxx.py 里直接增删相应的代码 ,
修改完成后,继续创建新的迁移脚本
python 文件 db migrate -m"新版本名(注释)"
更新数据库
python3 xxx.py db upgrade
更新完之后,其实就是提交操作,类似于 git 添加一个新的版本。但是,如果我们想返回历史的版本,
应该怎么操作呢?
先查看版本号
python xxx.py db history
想要返回的版本号。返回指定的版本
python xxx.py db downgrade(upgrade) 版本号
然后打开你的代码,可以发现他自动复原了!
5、什么是Flask-Script?
Flask-Script 用来生成 shell 命令;为在 Flask 里编写额外的脚本提供了支持。
这包括运行一个开发服务器,一个定制的 Python 命令行,用于执行初始化数据库、定时任务和其
他属于 web 应用之外的命令行任务的脚本。
Flask-Script 和 Flask 本身的工作方式类似。只需要定义和添加能从命令行中被 Manager 实例调用的
命令即可。
1、为什么使用Flask-Script?
Flask 的开发 Web 服务器支持很多启动设置选项,但只能在脚本中作为参数传给 app.run() 函数。这种方
式很不方便,传递设置选项的理想方式是使用命令行参数。
Flask-Scrip 就是这么一个 Flask 扩展,为 Flask 程序 添加一个命令行解析器 。
Flask-Script 自带了一组 常用选项 ,而且还 支持自定义命令 。
1)安装 Flask-Script?
pip install flask_script
2)如何配置 Flask-Script?
创建文件 manage.py 作为项目的入口文件。
无需把所有的命令都放在同一个文件里,例如,在一个大型项目中,可以把相关联的命令放在 不同的文件里。
# **** manage.py文件
from flask_script import Manager
app = Flask(__name__)
# Manager类将追踪所有的在命令行中调用的命令和处理过程的调用运行情况;
# configure your app
manager = Manager(app) if __name__ == "__main__":
# 将启动Manger实例接收命令行中的命令。
manager.run()
实现功能
python manage.py
python manage.py runserver
python manage.py runserver -h
python manage.py runserver -h '0.0.0.0' -p 8089
3)添加自定义命令的 3 种方式 :
网站参考 : https://flflask-script.readthedocs.io/en/latest/
使用 command 装饰器
定义 Command 的子类 ;
使用 option 装饰器
# **** manage.py文件
# 方法一:
@manager.command def hello():
return "hello"
# 方法二:
from flask_script import Command
class Hello(Command):
"prints hello world"
def run(self):
return "hello world"
manager.add_command('hello', Hello())
# 方法三:
@manager.option('-n', '--name', help='Your name')
def hello(name):
return "hello", name
4) 命令行拓展开发
Extension developers can easily create convenient sub-manager instance within their
extensions to make it easy for a user to consume all the available commands of an
extension.
Here is an example how a database extension could provide (ex. database.py):
# 添加其他的命令到manager里面来
from app.managerUtil.database import database_manager
# 自定义的数据库操作
manager.add_command('database', database_manager)
# flask-script集成的数据库操作
manager.add_command('db', MigrateCommand)
6、Flask常用钩子函数
1、什么是钩子函数?
在正常执行的代码前中后,强行插入执行一段你想要实现的功能的代码,这种函数就叫做钩子函数。钩
子函数就是等同于高速公路上的收费站,进高速之前给你一个卡,并检查你是否超重。离开之前收你,
也可以拦住你安检一下。
1、基础概念
request: Flask 的请求上下文,包含请求变量如 : method 、 args 、 form 、 values 、 endpoint 、
headers 、 remote_addr 都是比较常用的。
session:Flask 的请求上下文,用于存放用户的会话信息。
current_app:Flask 的应用上下文,返回当前 app 的方法和属性,可以勉强理解为类全局变量。
2、常用的钩子函数
before_first_request 处理第一次请求之前执行
只在第一次请求之前执行,也就是启动项目,不会执行,只会在第一次有人发起请求时,才会触发这个
钩子中的代码。
全局场景 : 可以带动一个异步执行的函数,进行一些健康指标的检查,如果发现有异常,则截断后续的请
求,将整个 Flask 应用停止。
# 服务器被第一次访问执行的钩子函数
@app.before_first_request
def first_request():
print("Hello World")
before_request 在每次请求之前执行 . 通常使用这个钩子函数预处理一些变量 , 视图函数可以更
好调用
这是最重要的一个钩子,在每次请求之前可以注入你要的逻辑的钩子。在 app 下的 before_request ,过
滤的是全部请求。结合 Blueprint 的 before_request ,则是过滤该蓝图下的请求。所以我们就可以进行
分层过滤,定制化过滤。
全局的场景包含:共享 session 的鉴权函数、请求黑白名单过滤、根据 endpoint 进行请求等。
# 在服务器接收的请求还没分发到视图函数之前执行的钩子函数
@app.before_request
def before_request():
# print("我勾住了每次请求")
user_id = session.get("user_id")
if user_id: g.user = "DaYe"
- teardown_appcontext 当 APP 上下文被移除之后执行的函数, 可以进行数据库的提交或者回滚
@app.teardown_appcontext
def teardown(exc=None):
if exc is None:
db.session.commit()
else:
db.session.rollback()
db.session.remove()
template_filter , 增加模板过滤器
@app.template_filter
def upper_filter(s):
return s.upper()
context_processor 上下文处理器 , 返回的字典可以在全部模板中使用
@app.context_processor()
def context():
# 必须返回一个字典
# hasattr(obj, attr) 判断obj是否有attr属性, 注意此时的attr应该是字符串
if hasattr(g, "user"):
return {"current_username": "DaYe"}
else:
# 注意: 必须返回一个字典
return {}
errorhander , 在发生一些异常时 , 比如 404 错误 , 就会自动调用对应的钩子函数
发生请求错误时 , 框架会自动调用相对钩子函数 , 并向钩子函数传入 error 参数
如果钩子函数没有定义 error 参数 , 就会报服务器错误
开发者可以通过 flask.abort 方法手动抛出异常 , 比如发现输入的参数错误可以使用
abort(404) 来解决
@app.errorhander(404)
def page_not_found(error):
return render_template("error400.html"), 404
@app.errorhander(500)
def server_error(error):
return render_template("error505.html"), 500
6、案例
1、路由和变量规则
from flask import Flask,request
app = Flask(__name__)
@app.route('/<int:userid>/')
def userinfo(userid):
return '正在查看用户%s的详细博客......'%(userid)
@app.route('/welcome/<string:username>')
def welcome(username):
return '欢迎访问%s用户的主页'%(username)
"""
https://movie.douban.com/top250?start=25&filter=
"""
@app.route('/top250')
def top250():
users = ['user%s' %(i) for i in range(100)]
#request存储用户请求页面的所有头部信息
print('客户端的用户代理:',request.user_agent)
print('请求页面的头部信息:',request.headers)
print('客户端的IP:',request.remote_addr) #127.0.0.1
print('客户端请求的参数详细信息:',request.args)
print('客户端HTTP请求方法:',request.method) #GET
#获取用户请求的url地址里面可以对应的value值;
start = int(request.args.get('start')) #25
user = request.args.get('user') #westos
#return 'top 250 显示数据开始:%s条 用户名:%s' %(start,user)
import json
return json.dumps(users[start:start+10])
if __name__ == '__main__':
app.run()
2、登录验证之http请求方法GET实现案例
"""
# 1.Http请求中常见的请求方式: GET POST
1). url可见性:
get,参数url可见;
post,url参数不可见
2). 数据传输上:
get,通过拼接url进行传递参数;
post,通过body体传输参数
3). 缓存性:
get请求是可以缓存的
post请求不可以缓存
4). 后退页面的反应
get请求页面后退时,不产生影响
post请求页面后退时,会重新提交请求
5). 传输数据的大小
get一般传输数据大小不超过2k-4k(根据浏览器不同,限制不一样,但相差不大)
post请求传输数据可以无限大。
6). 安全性: 原则上post肯定要比get安全。
# 2. 模版渲染
hello {{ name }}
name = westos hello westos
Flask和Django一样都配备了Jinja2模版引擎,可以使用render_template()租用模板方法来渲染模版。
# 3. 重定向和错误(redirect,error)
使用redirect()函数把用户重定向到其他地方。 '/bbs' ---> '/login'
使用abort()函数,放弃请求并返回错误代码。 # HTTP状态码: 404(4开头客户端错误), 200, 304, 500(5开头服务端错误)
"""
from flask import Flask, render_template, request, redirect
app = Flask(__name__)
@app.route('/')
def index():
return "<h1>主页</h1>"
@app.route('/login/') #表单
def login():
"""
一般情况, 不会直接把html文件内容直接返回;
而是将html文件保存到当前的templates目录中;
1). 通过render_template方法调用;
2). 默认情况下,Flask 在程序文件夹中的 templates1 子文件夹中寻找模板。
"""
return render_template('login.html')
@app.route('/login2/')
def login2():
# 获取用户输入的用户名
username = request.args.get('username', None)
password = request.args.get('password', None)
# 逻辑处理, 用来判断用户和密码是否正确;
if username == 'root' and password == 'redhat':
# 重定向到指定路由;
# 如果登录成功, 进入主页.
return redirect('/')
# return "登录成功"
else:
# return "登录失败"
# 如果登录失败, 重定向到登录界面重新登录;
return redirect('/login/')
if __name__ == '__main__':
app.run()
3、登录验证值http请求值POST
"""
# 1.自定义错误页面:
1). 为什么要自定义错误页面?
如果你在浏览器的地址栏中输入了不可用的路由,那么会显示一个状态码为 404 的错误页
面。现在这个错误页面太简陋、平庸.
2). 如何自定义错误页面?
像常规路由一样,Flask 允许程序使用基于模板的自定义错误页面。
最常见的错误代码有两个:
- 404,客户端请求未知页面或路由时显示;
- 500,有未处理的异常时显示。
"""
from flask import Flask, request, render_template, redirect, abort
app = Flask(__name__)
@app.route('/')
def index():
return "这是主页"
# 默认路由只支持get方法, 如何指定接受post方法?
@app.route('/login/', methods=['GET', 'POST'])
def login():
"""
1. 用户访问网址: http://xxxx/login, 返回登录的html页面;
方法method: GET
2. 用户在表单中填写信息,
<form action="/login/" method="POST">
3. 执行post提交的逻辑;
:return:
"""
if request.method == 'POST':
# 难点: post请求提交的数据如何获取? request.form
# 难点: get请求提交的数据如何获取? request.args
print(request.form)
username = request.form.get('username')
password = request.form.get('password')
# 如果用户名和密码正确, 跳转到主页;
if username == 'root' and password == 'redhat':
return redirect('/')
# 如果登录不正确, 则警告红色信息;还是在登录页面;
else:
# 可以给html传递变量
return render_template('login_post.html',
errMessages="用户名或者密码错误"
)
else:
# abort(500)
return render_template('login_post.html')
@app.route('/welcome/<string:username>')
def welcome(username):
app.logger.error("欢迎界面......")
return render_template('welcome.html', name=username)
@app.errorhandler(404)
def page_not_found(e):
return render_template("404.html"), 404
@app.errorhandler(500)
def internal_server_error(e):
return render_template("500.html"), 500
if __name__ == '__main__':
app.run()
4、Flask日志记录
"""
格式化中的常用参数如下:
%(name)s Logger的名字
%(levelno)s 数字形式的日志级别
%(levelname)s 文本形式的日志级别
%(pathname)s 调用日志输出函数的模块的完整路径名,可能没有
%(filename)s 调用日志输出函数的模块的文件名
%(module)s
调用日志输出函数的模块名
%(funcName)s
调用日志输出函数的函数名
%(lineno)d
调用日志输出函数的语句所在的代码行
%(created)f
当前时间,用UNIX标准的表示时间的浮 点数表示
%(relativeCreated)d
输出日志信息时的,自Logger创建以 来的毫秒数
%(asctime)s
字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒
%(thread)d
线程ID。可能没有
%(threadName)s
线程名。可能没有
%(process)d
进程ID。可能没有
%(message)s
用户输出的消息
"""
from flask import Flask
import logging
app = Flask(__name__)
# 日志系统配置, 设置文件存放位置;
handler = logging.FileHandler('app.log', encoding='UTF-8')
# 设置日志文件存储格式
logging_format = logging.Formatter(
'%(asctime)s - %(levelname)s - %(filename)s - %(funcName)s - %(lineno)s - %(message)s')
# 将日至文件处理对象和日至格式绑定,
handler.setFormatter(logging_format)
handler.setLevel('DEBUG')
# 将日志格式和app绑定;
app.logger.addHandler(handler)
@app.route('/')
def index():
app.logger.debug('hello')
app.logger.error('error')
app.logger.exception('exception')
app.logger.info('hello')
return 'index'
if __name__ == '__main__':
app.run(debug=True, port=8000)
其中,app.log会自动生成