Flask入门知识点小结
一、Flask基础
1. 基于Flask框架可快速建立起后端服务器:
app = Flask(__name__)
在主函数里运行服务器
if name = "__main__":
app.run(debug=True, host="0.0.0.0", post="5000")
- 127.0.0.1:回环地址,不会到达外部网络接口,ip包不离开本机
- 0.0.0.0:代表当前设备的ip,从而外部网络可通过本机IP访问服务器
2、 实现URL与视图函数的映射
@app.route('/index', methods=["GET", "POST"])
def index():
pass
- 绑定url和相应的视图函数,从而用户输入url时会调用相应的视图函数
1)URL: 统一资源定位器
- 组成:协议+主机+端口+路径 ( http://127.0.0.1:5000/index)
- 由客户端(浏览器)据此寻找相应网页
2)视图函数:接受客户端Web请求并给客户返回Web响应
i) 视图函数返回值
返回值可以是HTML模板、重定向网址、404错误,XML文档……
-
返回HTML模板:通过render_template()
from flask import ..., render_template ...... return render_template("index.html") -
返回重定向网址:通过redirect()函数
(可通过url_for获取视图函数对应的url)
from flask import ..., redirect, url_for ...... return redirect(url_for("auth.login")) -
返回成功或失败响应的Json文件:通过jsonify实现
from flask import ..., jsonify ...... return jsonify(a="1")
ii) Http协议的Web请求方式:GET、POST、PUT、DELETE
- GET请求:
- 通过URL来传递数据(......?name=asd)
- 一般用于客户端从服务器获取资源
- POST请求:
- 一般用于客户端向服务器提交资源(表单等数据)
3)向视图函数中传递参数:三种方法
- 在url上传递(可限制类型)
@app.route('/blog/<int:blog_id>')
def blog_detail(blog_id):
return "您将访问的博客是:%d" % blog_id
-
GET请求、POST请求:通过request对象
from flask import request request.get("a") request.form # POST请求
4) 限制用户Web请求方式:设置methods参数
@app.route('/index', methods=["GET"]) # 只接收GET请求
def index():
pass
3. 前端部分
1)前端基础:
- HTML:页面内容
- CSS:样式
- Bootstrap库
- JavaScript:脚本语言
- jQuery库
2)基于Jinja2模板引擎的HTML模板
Flask底层集成了Jinja2模板引擎,因此可直接调用render_template()函数渲染模板(但实际是基于Jinja2实现的)
功能包括(传递给render_template()函数的HTML模板,还要在底层由Jinja2库进行相应操作后才发送给用户,因此可以在这里写一些HTML常规语法没有的语法功能):
i) 给HTML模板传参
可以传递给HTML模板数据(包括用户自定义的对象等)
Python中
@app.route('/blog2/<blog_id>')
def index(blog_id):
user = User(username="asdf", email="asdf@qwe")
return render_template("index.html", user=user)
HTML模板中:通过双花括号传递参数、访问对象属性
<h1>您访问的博客详情是:{{ user.username }}</h1>
ii) HTML模板中使用筛选器
核心作用;可以在HTML模板中使用函数
- 过滤器相当于一个函数,通过|调用,例如
{{name|length}}即为length(name),其中length即是模板内置的函数 - 过滤器先根据自己功能返回值,然后将该值渲染到模板页面中
模板过滤器
常用模板过滤器:
- 字符串操作
- 数值操作
- 列表操作
- 字典列表操作
自定义过滤器
-
先自定义过滤器函数(例如my_filter)
def Str_Is(value): return "str is: " + str(value) -
将此过滤器加给app
第二个参数为调用时所用的名字
app.add_template_filter(Str_Is, "Str_Is") -
在HTML模板中调用即可
<div>测试自定义筛选器:{{name|Str_Is}}</div>
iii) HTML模板中使用控制语句
条件语句(一定要加上endif作为结束标识)
{% if user_age>18 %}
<div>有18岁了</div>
{% elif user_age>16 %}
<div>有16岁了</div>
{% else %}
<div>不到16</div>
{% endif %}
循环语句(一定要加上endfor作为结束标识)
{% for i in range(5) %}
<div>{{ i }}</div>
{% endfor %}
{% for book in books %}
<div>{{ book }}</div>
{% endfor %}
iv) 在HTML模板中导入静态库
由于HTML文件是发在用户浏览器端执行,因此不能直接使用服务器本地地址
通过url_for获取静态库url:
<script
src="{{ url_for('static', filename='jQuery.js')}}">
</script>
v) HTML模板继承
为了避免对相同内容的重复书写
父模板中(block后为此block的名字,可以自行修改)
{% block title %}父模板内容{% endblock %}
子模板中:
-
开头:
{% extends "base.html" %} -
使用处:(根据block名调用)
{% block title %} 子模板内容blablbla {% endblock %}
呈现出的结果:子模板中,block间的内容相应替换为父模板
4. 程序的上下文
上下文:可理解为一种容器,对于Flask服务器,Flask中上下文保存了Flask程序运行过程中的一些信息
Flask程序中有两种上下文类型:请求上下文、应用上下文
- 请求上下文:保存客户端与服务器交互的数据,例如
- request:封装了Http请求内容
- session:用于记录请求会话中的信息,例如用户信息等
- 应用上下文:保存Flask程序运行中的一些信息,例如
- current_app:表示当前运行程序的若干事项
- g:服务器可用此存储变量,方便各视图函数交互
Flask基层是基于wekzeug对象实现,
- 上下文内容都绑定在werkzeug.local.Local,从而同一个对象可实现多个线程中隔离
由于werkzeug对象可能包括多个Flask对象(?),因此在运行时维护一个栈,栈顶为当前用到的上下文相关数据
-
在视图函数中,Flask底层会自动实现将所需上下文推入栈中
-
在视图函数外执行操作,则需要手动对上下文进行入栈、出栈操作
(以下两种方法都可)
app_context = app.app_context() app_context.push() with app.app_context(): pass
二、服务器端与数据库交互
1. 基于SQLAlchemy实现ORM映射
1)ORM映射:把数据库映射成类和对象的形式
flask_sqlalchemy实现了对一系列数据库的SQL映射(包括MySQL,sqList等),从而可以用纯python的方法操作数据库(而不用去管数据库本身的那些sql语句)
- 表:映射为类
- 表中的字段:映射为类中字段(Column)
- 表中数据:以类的实例(对象)表示
2)Flask中具体应用方法
i) 连接数据库
# 设置数据库基本参数等
app.config['SQLALCHEMY_DATABASE_URI'] = ...
db = SQLAlchemy(app=app)
ii) 创建表
创建表:
class User(db.Model): # 继承自db.Model
__tablename__ = "user" # 设置表名
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
username = db.Column(db.String(100), nullable=False)
password = db.Column(db.String(100), nullable=False)
表中关联外键方法
(有了backref,在user对象可以调用user1.articles获取全部关联的文章)
class ArticleFOR(db.Model):
__tablename__ = "article"
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
title = db.Column(db.String(200), nullable=False)
author_id = db.Column(db.Integer, db.ForeignKey("user.id"))
author = db.relationship("User", backref="articles")
iii) 数据库中的增删改查
查到->操作(add/delete/...)->提交(commit)
增加表项
user = User(username="qwe", password="asdf")
db.session.add(user)
db.session.commit()
查找表项
-
根据id(primary_key)查找
get()函数user = User.query.get(1) -
根据相应属性查找
filter_by()函数users = User.query.filter_by(username="qwe")-
返回一个Query对象,类似数组,取:
print(users[3].password) users.first() users.all()
-
-
根据部分名查找(常用于搜索操作)
filter()函数q ="q" questions = User.query.filter(User.title.contains(q)).all()
删除表项
user = User.query.filter_by(username="罗贯中").first()
db.session.delete(user)
db.session.commit()
修改表项
user = User.query.filter_by(username="qwe").first()
user.username = "罗贯中"
db.session.commit()
2. 数据库迁移
目的:可以更改数据库格式的同时保留数据库原有数据信息
通过flask_migrate实现
先构建一个迁移对象(?)
migrate = Migrate(app, db)
然后在命令行中执行以下指令
flask db init: 只需做一次, 生成migration文件夹,包含一系列迁移脚本
flask db migrate -m"添加备注信息":会在version中生成一个迁移脚本
flask db upgrade: 运行迁移脚本,同步到数据库中
三、密码加密与验证
可以通过werkzeug.security实现
一个Hash加密方式:
from werkzeug.security import generate_password_hash, check_password_hash
# 加密
generate_password_hash(password)
# 检查(返回结果为布尔值)
check_password_hash(hasuser.password, password)
四、表单验证功能
可以通过wtforms包实现,主要验证前端提交的数据是否满足一些基本要求
验证方法:
-
构建验证表单类
class RegisterForm(wtforms.Form):-
字段值设置:构造为wtforms包中相应的类(传入参数为所选取的wtforms包内置验证器)
email = wtforms.StringField (validators=[Email(message="邮箱格式错误")]) -
可自定义验证方法(会被自动调用)
def validate_email(self, filed): email = filed.data # 通过.data获取当前字段的值 user = UserModel.query.filter_by(email=email).first() if user: raise wtforms.ValidationError(message="该邮箱已经被注册")
-
-
应用时,传入require.form,构造验证对象
form = RegisterForm(request.form) if form.validate(): # 验证 email = form.email.data # 获取表单中的数据
常用的wtform包中工具
构造字段:
wtforms.StringField(validators=...)wtforms.IntegerField(validators=...)
验证器:
Email(message="...")验证是否为邮箱格式,message为报错信息Length(min=4, max=4, message="...")长度EqualTo("password", message="...")第一个参数要与之等的字段名InputRequired(message="...")要求必须有输入
报错信息:
wtforms.ValidationError(message="...")- 验证失败抛出异常,要记得捕获
五、发送邮件功能
可以基于flask_mail包实现
i) 构建一个用于发送邮件的Mail对象
构建对象
from flask_mail import Mail
mail = Mail()
给Mail对象添加参数
# 邮箱配置
MAIL_SERVER = "smtp.qq.com"
MAIL_USE_SSL = True
MAIL_PORT = 465
MAIL_USERNAME = "1648643704@qq.com"
MAIL_PASSWORD = "vchlffbqhdqcehdj" # 通过开启SMPT/POP3,从而运行第三方软件进行登录
MAIL_DEFAULT_SENDER = "1648643704@qq.com"
ii) 构建邮件内容message
from flask_mail import Message
message = Message(subject="CY验证码", # 邮件主题
recipients=[email], # 邮箱地址
body="...") # 邮件内容
iii) 发送邮件
mail.send(message)
六、用户登录状态:cookie与session
通过上下文session和g
1)设置登录状态:设置session参数
在flask中,session是包装在cookie中的(?,在cookie中会对其加密。
cookie:身份的象征,由服务器发给浏览器,浏览器保存,每次浏览器发送请求时附带cookie,从而证明身份
登录时设置session方法:
session['user_id'] = user.id
(设置之后服务器会发给浏览器一个cookie)
2)每次进入视图函数/模板前提取session中信息
方法:通过设置钩子函数,提取session中的信息,并在应用上下文g中加入更详细的用户信息,从而其他视图函数可以检查,若含有,则说明登录了
钩子函数
Flask框架中的一种函数,每次调用视图函数前都会先执行此函数(从而避免每次重复使用相同代码)
除了下面用到的before_request外,还有before_first_request / after_request,顾名思义
@app.before_request # 装饰器
def my_before_request():
user_id = session.get("user_id") # 解密的过程flask会做好
if user_id: # 即
user = UserModel.query.get(user_id)
# setattr python内置函数
setattr(g, "user", user)
else:
setattr(g, "user", None)
*上下文处理器函数
每次模板执行前都会先调用此函数,从而可以用来设置模板中登录栏的状态
@app.context_processor
def my_context_processor():
return {"user": g.user} # 从而在之后所有模板中都可以使用user变量
3)对于需要登录状态的页面,需先检验
通过装饰器来重用每次的检验过程
自己编写一个用于验证登录状态的装饰器函数:
def login_required(func):
@wraps(func) # 为了保留函数原来的信息
# func(1,2,c=3) -> 1,2存储到args中,c=3存储到kwargs中
def inner(*args, **kwargs):
if g.user:
return func(*args, **kwargs)
else:
print("请先登录")
return redirect(url_for("auth.login"))
return inner
在每个需要登录的视图函数前应用此装饰器
@bp.route('/qa/public_question', methods=["GET", "POST"])
@login_required
def public_question():
pass
七、客户端与服务器端交互
(如点击发送验证码功能)
1. Ajax请求
Ajax是一种用于创建快速动态网页的技术,通过后台与服务器进行少量交互,Ajax可以使网页实现异步更新(即不重新加载整个网页的情况下,对网页某部分进行更新)
Ajax工作原理:发生相应事件时,发送HTTP请求给服务器,预先绑定了回调函数,会根据服务器返回结果进行相应更新操作
(从而既完成了与服务器交互,有可根据服务器返回结果部分更新网页)
- 当发生需要异步更新事件时(例如用户点击了"发送邮箱验证码"按钮),客户端(浏览器)创建XMLHttpRequest对象(即一个异步调用对象)
- 客户端创建一个HTTP请求,会指定url、method等基础信息
- 绑定收到服务器相应后的状态变化函数(例如success、fail)
- 发送请求,获取到异步调用返回的数据
- 根据事先绑定的回调函数,执行相应的操作(基于JS和DOM实现局部更新)
具体应用方法(基于jQuery库的JS脚本):
-
要求对服务器会发回来怎样的JSON数据格式及意义了解
$.ajax({ url: "/auth/captcha/email?email=" + email, method: "GET", // result即后端传过来的那个json数据 success: function (result) { console.log(result); let code = result['code'] // 根据返回数据进行鉴别 if (code == 200) { .... alert("邮箱验证码已发送"); } else { .... alert("系统繁忙,请稍后再次发送"); } }, fail: function (error) { console.log(error); } }) -
服务器端需要通过jsonify等方法构造返回的json文件
@bp.route('/captcha/email') def get_email_captcha(): ..... res = jsonify({"code": 200, "message": "", "data": None}) return res
八、整体架构设计
1. 蓝图的应用
基于flask中的Blueprint包
在蓝图包blueprints包中创建蓝图
bp = Blueprint(name="auth",
import_name=__name__, # 被其他包引入时的名字
url_prefix="/auth")
蓝图中的视图函数
@bp.route('/login', methods=['GET', 'POST'])
def login():
pass
在app.py中应用:
from blueprints.auth import bp as auth_bp
from blueprints.qa import bp as qa_bp
导入蓝图:
# 导入蓝图
app.register_blueprint(auth_bp)
app.register_blueprint(qa_bp)
在其他文件中通过url_for获取时:
url_for("qa.public_question")
2. 防止循环引用
对于db、mail,可以现在exts.py中构造,在app.py中为它们加入参数,从而可避免模型的循环引用
可以设置一个config.py文件,参数都加在这里面,然后
app.config.from_object(config)
浙公网安备 33010602011771号