1.WTForms表单验证基本使用
flask-wtf是一个简化了WTForms操作的一个第三方库,WTForms表单的两个主要的功能jiushi就是验证用户提交数据的合法性以及渲染模板。当然还包括其他的功能:CSRF保护,文件上传等等。安装flask-wtf也会默认安装wtforms。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/register" method="post"> <table> <tbody> <tr> <td>姓名:</td> <td><input name="username" type="text"></td> </tr> <tr> <td>密码:</td> <td><input name="password" type="password"></td> </tr> <tr> <td>重复密码:</td> <td><input name="repeat_passwd" type="password"></td> </tr> <tr> <td></td> <td><input type="submit" value="立即注册"></td> </tr> </tbody> </table> </form> </body> </html>
from flask import Flask, request, render_template from wtforms import Form, StringField from wtforms.validators import Length, EqualTo app = Flask(__name__) class RegisterForm(Form): # 我们这里的左值,就是html里面的name="username",这里一定要保持一直,否则不会验证 # 这里传入一个列表,因为验证条件会有多个,所以以列表形式传值,这里表示长度为6到10位 username = StringField(validators=[Length(min=6, max=10)]) password = StringField(validators=[Length(min=6, max=10)]) # 这里的repeat_passwd必须要和password保持一致,所以这里的Length也可以不要,まぁいいや repeat_passwd = StringField(validators=[Length(min=6, max=10), EqualTo("password")]) @app.route("/register", methods=["GET", "POST"]) def register(): if request.method == 'GET': return render_template("register.html") else: # 直接将request.form丢进去就可以了 form = RegisterForm(request.form) if form.validate(): return "登陆成功" else: # 没通过的话,那么错误信息会存储在form.errors里面 return f"登录失败:{form.errors}" if __name__ == '__main__': app.run(host="localhost", port=8888)
但是这种报错信息似乎显得不友好,因此我们也可以自己指定
class RegisterForm(Form): # 我们这里的左值,就是html里面的name="username",这里一定要保持一直,否则不会验证 # 这里传入一个列表,因为验证条件会有多个,所以以列表形式传值,这里表示长度为6到10位 username = StringField(validators=[Length(min=6, max=10, message="用户名必须在6到10位")]) password = StringField(validators=[Length(min=6, max=10, message="密码必须在6到10位")]) # 这里的repeat_passwd必须要和password保持一致,所以这里的Length也可以不要,まぁいいや repeat_passwd = StringField(validators=[Length(min=6, max=10, message="密码必须在6到10位"), EqualTo("password", message="两次输入的密码不一致")])
2.WTForms常用验证器
常用的验证器:
数据发送过来,经过表单验证,因此需要验证器来进行验证,以下是一些常用的验证器
·Email:验证上传的数据是否为邮箱
·EqualTo:验证上传的数据是否和另外一个字段相等,常用的就是密码和确认密码两个字段是否相等
·LnputRequired:表示该字段为必填项,只要填了就通过,不填就是失败
·Length:长度限制,有min和max两个值进行限制
·NumberRange:数字的区间,有mix和max两个值进行限制,而且必须是数字,如果处在这两个数字之间则满足
·Regexp:自定义正则表达式
·URL:必须要是url的形式
·UUID:必须是UUID的形式
from flask import Flask, request, render_template from wtforms import Form, StringField from wtforms.validators import Length, EqualTo, Email, InputRequired, NumberRange, Regexp, URL, UUID app = Flask(__name__) class RegisterForm(Form): email = StringField(validators=[Email()]) username = StringField(validators=[InputRequired()]) age = StringField(validators=[NumberRange(min=18, max=60)]) phone = StringField(validators=[Regexp(r"1\d{10}")]) url = StringField(validators=[URL()]) uuid = StringField(validators=[UUID()])
没有什么技术难度,具体不再演示了
3.自定义表单验证器
from flask import Flask, request, render_template from wtforms import Form, StringField from wtforms.validators import Length, EqualTo, Email, InputRequired, NumberRange, Regexp, URL, UUID, ValidationError app = Flask(__name__) class RegisterForm(Form): # 如何自定义表单,比如我们在html里面有一个name="username" # 那么就需要定义一个函数叫做,validate_username,当验证username的时候,会自动执行相应的函数 username = StringField(validators=[InputRequired()]) def validate_username(self, field): # field.data,就是我们在html中获取的值 print(type(field)) # <class 'wtforms.fields.core.StringField'> # 如果全部username全部是数字,那么不允许 if set(field.data) < set("123456789"): raise ValidationError("用户名不能全为数字") @app.route("/register", methods=["GET", "POST"]) def register(): if request.method == 'GET': return render_template("register.html") else: # 直接将request.form丢进去就可以了 form = RegisterForm(request.form) if form.validate(): return "登陆成功" else: # 没通过的话,那么错误信息会存储在form.errors里面 return f"登录失败:{form.errors}" if __name__ == '__main__': app.run(host="localhost", port=8888)
4.使用wtforms渲染模板
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .text_color{ color: darkseagreen; } </style> </head> <body> <table> <tbody> <tr> <td>{{form.username.label}}</td> <td>{{form.username(class='text_color')}}</td> </tr> <tr> <td>{{form.age.label}}</td> <td>{{form.age()}}</td> </tr> <tr> <td>{{form.remember.label}}</td> <td>{{form.remember()}}</td> </tr> <tr> <td>{{form.tags.label}}</td> <td>{{form.tags()}}</td> </tr> <tr> <td></td> <td> <input type="submit" value="提交"> </td> </tr> </tbody> </table> </body> </html>
from flask import Flask, request, render_template from wtforms import Form, StringField, BooleanField, SelectField from wtforms.validators import Length, EqualTo, Email, InputRequired, NumberRange, Regexp, URL, UUID, ValidationError app = Flask(__name__) class IndexForm(Form): # 第一个参数指定之后,会自动传到html中的form.username.label,如果不指定,那么自动为左值并且首字母大写 # 而form.username则是一个input标签,这里的左值username到html中就会变成,name="username" username = StringField("用户名:", validators=[InputRequired()]) age = StringField("年龄:", validators=[InputRequired()]) remember = BooleanField("记住我:") tags = SelectField("选项:", choices=[("1", "古明地觉"), ("2", "椎名真白"), ("3", "四方茉莉")]) @app.route("/index", methods=["GET", "POST"]) def index(): if request.method == "GET": form = IndexForm() return render_template("index.html", form=form) else: form = IndexForm() form = IndexForm(request.form) if __name__ == '__main__': app.run(host="localhost", port=8888)
5.上传文件以及访问上传的文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/upload" method="post" enctype="multipart/form-data"> <table> <tr> <td>头像:</td> <td><input type="file" name="avatar"></td> </tr> <tr> <td>描述:</td> <td><input type="text" name="describe"></td> </tr> <tr> <td></td> <td><input type="submit" value="提交"></td> </tr> </table> </form> </body> </html>
from flask import Flask, request, render_template import os app = Flask(__name__) @app.route("/upload", methods=["GET", "POST"]) def upload(): if request.method == 'GET': return render_template("upload.html") else: describe = request.form.get("describe") avatar = request.files.get("avatar") # avatar就是我们所获取的文件 # avatar.filename则是文件名,那么如何将文件保存起来呢?可以直接使用save # 由于直接使用用户获取的文件名比较危险,那么可以from werkzeug.utils import secure_filename,然后进行转化 avatar.save(os.path.join(os.path.dirname(__file__), "images", avatar.filename)) return f"{avatar.filename}上传成功, 描述信息为{describe}" app.run(host="localhost", port=8888)
而且我们获取了图片如何让用户访问呢?
@app.route("/images/<filename>") def get_image(filename): if os.path.exists(os.path.join(os.path.dirname(__file__), "images", filename)): # send_from_directory表示将图片直接返回 # 接收两个参数,一个是图片的目录,一个是图片的名字 return send_from_directory(os.path.join(os.path.dirname(__file__), "images"), filename) else: return "没有这个玩意儿······"
6.使用flask-wtf验证上传的文件
之前我们接收用户上传的文件,也没有进行判断,比如说用户上传头像,应该是一张图片,可如果用户上传的是txt,或者py文件怎么办呢?这时候我们应该对用户上传的文件进行一个判断.
我们这里新建一个forms.py文件,用于存放form表单。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/upload" method="post" enctype="multipart/form-data"> <table> <tr> <td>头像:</td> <td><input type="file" name="avatar"></td> </tr> <tr> <td>描述:</td> <td><input type="text" name="describe"></td> </tr> <tr> <td></td> <td><input type="submit" value="提交"></td> </tr> </table> </form> </body> </html>
from wtforms import Form, FileField, StringField # FileRequired表示这个文件必须上传,FileAllowed表示允许的文件格式 from flask_wtf.file import FileRequired, FileAllowed from wtforms.validators import InputRequired class UploadForm(Form): avatar = FileField(validators=[FileRequired(), FileAllowed(["jpg", "png", "gif"])]) describe = StringField(validators=[InputRequired()])
from flask import Flask, request, render_template from forms import UploadForm from werkzeug.datastructures import CombinedMultiDict app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello World!' @app.route('/upload', methods=["GET", "POST"]) def get_img(): if request.method == 'GET': return render_template("upload.html") else: # 我们既有文件类型,还有一般的表单类型。因此需要把两者组合在一块 form = UploadForm(CombinedMultiDict([request.form, request.files])) if form.validate(): avatar = request.files.get("avatar") describe = request.form.get("describe") ''' 此外还可以通过另一种方式获取 <input type="file" name="avatar"> <input type="text" name="describe"> avatar = form.avatar.data describe = form.describe.data 效果是一样的 ''' return f"上传成功,上传的文件为:{avatar.filename}, 描述信息为:{describe}" else: return f"上传失败:{form.errors}" if __name__ == '__main__': app.run()
7.cookie的基本概念
pass
8.flask设置和删除cookie
from flask import Flask, Response app = Flask(__name__) @app.route('/') def hello_world(): # 之前说过返回的其实一个Response对象,而我们之所以能直接返回字符串,是因为flask会自动帮我们转化成Response对象 # 但是如果我们想设置cookie必须手动创建Response对象 res = Response("hello world!") res.set_cookie(key="satori", value="elegant") return res if __name__ == '__main__': app.run()
那么如何删除cookie呢?
@app.route("/del") def delete(): # 还可以删除cookie,注意删除cookie并不会真正意义上将cookie上删除。而是将cookie的值设为空,有效期改成失效。 # 真正清楚cookie是由浏览器来做的 res = Response("delete cookie") res.delete_cookie("satori") return res
9.flask设置cookie的有效时间
@app.route('/') def hello_world(): # 如何设置cookie的有效时间呢?有两个参数可以设置 # 一个参数是max_age,表示距离现在多少秒之后过期,比方说max_age=60,则60秒之后过期 # 还有一个是expires,需要传入一个datetime类型,我们python自带的就可以,表示过期时间。并且必须传入格林尼治时间,浏览器会自动加八个小时 # expires = datetime.datetime(year=2018, month=11, day=20, hour=0, minute=0, second=0),那么会在2018-11-20 08:00过期 # 所以我们需要在设置的时候,要比期望的时间少8个小时 # 此外expires也可以使用相对时间,expires=datetime.now() + datetime.timedelta(days=30, hour=16),表示31天之后过期 # 如果两者都设置了,那么以max_age为标准,如果都没设置,那么当关闭浏览器时(是关闭浏览器),cookie失效 res = Response("hello world!") res.set_cookie(key="satori", value="elegant", max_age=None, expires=None) return res
10.flask设置cookie的有效域名
from flask import Blueprint bp = Blueprint("cms", __name__, subdomain="cms") @bp.route('/') def index(): return "cms page"
from flask import Flask, Response from book import bp app = Flask(__name__) app.register_blueprint(bp) app.config["SERVER_NAME"] = "satori.com:5000" @app.route('/') def hello_world(): return "hello world" if __name__ == '__main__': app.run()
from flask import Flask, Response from book import bp app = Flask(__name__) app.register_blueprint(bp) app.config["SERVER_NAME"] = "satori.com:5000" @app.route('/') def hello_world(): res = Response("hello world") res.set_cookie("satori", "elegant", max_age=1000, domain=".satori.com") return res if __name__ == '__main__': app.run()
from flask import Blueprint, request bp = Blueprint("cms", __name__, subdomain="cms") @bp.route('/') def index(): # 那么如何获取cookie呢? satori = request.cookies.get("satori") print(satori) return "cms page"
11.session的基本概念
pass
12.flask设置session
from flask import Flask, session import secrets app = Flask(__name__) # 设置session,则必须要有一个SECRET_KEY app.config["SECRET_KEY"] = secrets.token_bytes(20) @app.route('/') def hello_world(): # 导入session,会自动交给浏览器 session["name"] = "satori" return 'Hello World!' if __name__ == '__main__': app.run()
@app.route("/get_session") def get_session(): session_value = session.get("name") return session_value or "没有获取到session"
@app.route("/delete_session") def delete_session(): return f'已删除session:{session.pop("name")}' or "没有这个session"
成功删除session:satori,记住一定要先访问,localhost:5000获取session,然后访问delete_session才能删除
from flask import Flask, session import secrets app = Flask(__name__) # 设置session,则必须要有一个SECRET_KEY app.config["SECRET_KEY"] = secrets.token_bytes(20) @app.route('/') def hello_world(): # 我们也可以设置多个session session["name"] = "satori" session["age"] = 18 session["anime"] = "东方地灵殿" return 'Hello World!' @app.route("/get_session") def get_session(): session_name = session.get("name") session_age = session.get("age") session_anime = session.get("anime") if session_name and session_age and session_anime: return f"session:{session_name}--{session_age}--{session_anime}" else: return "没有获取到session" @app.route("/delete_session") def delete_session(): # 也可以一次行删除所有session,因为导入的session继承自dict sess = dict(session) session.clear() return f"删除session之前:{sess}, 删除之后:{dict(session)}" if __name__ == '__main__': app.run()
设置cookie的过期时间
from flask import Flask, session import secrets import datetime app = Flask(__name__) app.config["SECRET_KEY"] = secrets.token_bytes(20) # 设置cookie有两种 # 1. app.config["PERMANENT_SESSION_LIFETIME"] = datetime.timedelta(hours=2),两小时后过期 @app.route('/') def hello_world(): session["name"] = "satori" session["age"] = 18 session["anime"] = "东方地灵殿" # 2. session.permanent = True, 表示session默认为31天之后过期 return 'Hello World!' if __name__ == '__main__': app.run()
13.csrf攻击原理
14.实战项目-中国工商银行注册功能
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>中国工商银行首页</title> </head> <body> <h1>欢迎来到中国工商银行</h1> <ul> <li><a href="{{ url_for('register') }}">立即注册</a></li> </ul> </body> </html>
register.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>中国银行注册页面</title> </head> <body> <form action="/register" method="post"> <table> <tbody> <tr> <td>邮箱:</td> <td><input type="email" name="email"></td> </tr> <tr> <td>用户名:</td> <td><input type="text" name="username"></td> </tr> <tr> <td>密码:</td> <td><input type="password" name="password"></td> </tr> <tr> <td>重复密码:</td> <td><input type="password" name="repeat_password"></td> </tr> <tr> <td>余额:</td> <td><input type="text", name="deposit"></td> </tr> <tr> <td></td> <td><input type="submit" value="立即注册"></td> </tr> </tbody> </table> </form> </body> </html>
exts.py
from flask_sqlalchemy import SQLAlchemy db = SQLAlchemy()
models.py
from exts import db class User(db.Model): __tablename__ = "user" id = db.Column(db.Integer, primary_key=True, autoincrement=True) email = db.Column(db.String(50), nullable=False) username = db.Column(db.String(50), nullable=False) password = db.Column(db.String(50), nullable=False) deposit = db.Column(db.Float(50), default=10)
manage.py
from flask_script import Manager from icbc import app from flask_migrate import Migrate, MigrateCommand from exts import db from models import User manager = Manager(app) Migrate(app, db) manager.add_command("db", MigrateCommand) if __name__ == '__main__': manager.run()
forms.py
from wtforms import Form, StringField, FloatField from wtforms.validators import Length, EqualTo, Email, InputRequired class RegisterForm(Form): email = StringField(validators=[Email()]) username = StringField(validators=[Length(6, 20)]) password = StringField(validators=[Length(6, 20)]) repeat_password = StringField(validators=[EqualTo("password")]) deposit = FloatField(validators=[InputRequired()])
icbc.py主程序
from flask import Flask, render_template, request, views from forms import RegisterForm from exts import db from models import User import config app = Flask(__name__) app.config.from_object(config) db.init_app(app) # 这个和db = SQLAlchemy(app)效果是一样的 @app.route('/') def index(): return render_template("index.html") class RegisterView(views.MethodView): def get(self): return render_template("register.html") def post(self): form = RegisterForm(request.form) if form.validate(): email = form.email.data username = form.username.data password = form.password.data deposit = form.deposit.data user = User(email=email, username=username, password=password, deposit=deposit) db.session.add(user) db.session.commit() return "注册成功" else: return f"注册失败,{form.errors}" app.add_url_rule("/register", view_func=RegisterView.as_view("register")) if __name__ == '__main__': app.run()
执行python manage.py db migrate 然后执行python manage.py db upgrade,然后会发现数据库多了一张user表
访问localhost:5000
点击立即注册之后,显示注册成功,我们查看一下数据库
发现数据已经被添加到数据库里面了
15.实战项目-中国工商银行登录和转账实现
首页,index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>中国工商银行首页</title> </head> <body> <h1>欢迎来到中国工商银行</h1> <ul> <li><a href="{{ url_for('register') }}">立即注册</a></li> <li><a href="{{ url_for('login') }}">立即登录</a></li> <li><a href="{{ url_for('transfer') }}">立即转账</a></li> </ul> </body> </html>
注册页面,register.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>中国银行注册页面</title> </head> <body> <form action="/register" method="post"> <table> <tbody> <tr> <td>邮箱:</td> <td><input type="email" name="email"></td> </tr> <tr> <td>用户名:</td> <td><input type="text" name="username"></td> </tr> <tr> <td>密码:</td> <td><input type="password" name="password"></td> </tr> <tr> <td>重复密码:</td> <td><input type="password" name="repeat_password"></td> </tr> <tr> <td>余额:</td> <td><input type="text", name="deposit"></td> </tr> <tr> <td></td> <td><input type="submit" value="立即注册"></td> </tr> </tbody> </table> </form> </body> </html>
登录页面,login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>中国工商银行登录页面</title> </head> <body> <form action="/login" method="post"> <table> <tbody> <tr> <td>邮箱:</td> <td><input name="email" type="email"></td> </tr> <tr> <td>密码:</td> <td><input name="password" type="password"></td> </tr> <tr> <td></td> <td><input type="submit" value="立即登录"></td> </tr> </tbody> </table> </form> </body> </html>
转账页面,transfer.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/transfer" method="post"> <table> <tbody> <tr> <td>转到邮箱:</td> <td><input type="email" name="email"></td> </tr> <tr> <td>转账金额:</td> <td><input type="text" name="money"></td> </tr> <tr> <td></td> <td><input type="submit" value="立即转账"></td> </tr> </tbody> </table> </form> </body> </html>
py文件只有forms.py和icbc.py发生了变化,其他的没变
form.py
from wtforms import Form, StringField, FloatField from wtforms.validators import Length, EqualTo, Email, InputRequired, NumberRange from models import User class RegisterForm(Form): email = StringField(validators=[Email()]) username = StringField(validators=[Length(6, 20)]) password = StringField(validators=[Length(6, 20)]) repeat_password = StringField(validators=[EqualTo("password")]) deposit = FloatField(validators=[InputRequired()]) class LoginForm(Form): email = StringField(validators=[Email()]) password = StringField(validators=[Length(6, 20)]) def validate(self): result = super(LoginForm, self).validate() if not result: return False email = self.email.data password = self.password.data user = User.query.filter(User.email == email, User.password == password).first() if not user: self.email.errors.append("邮箱或密码错误") return True class TransferForm(Form): email = StringField(validators=[Email()]) money = FloatField(validators=[NumberRange(min=1, max=1000)])
icbc.py
from flask import Flask, render_template, request, views, session, redirect, url_for from forms import RegisterForm, LoginForm, TransferForm from exts import db from models import User import config app = Flask(__name__) app.config.from_object(config) db.init_app(app) # 这个和db = SQLAlchemy(app)效果是一样的 @app.route('/') def index(): return render_template("index.html") # 注册 class RegisterView(views.MethodView): def get(self): return render_template("register.html") def post(self): form = RegisterForm(request.form) if form.validate(): email = form.email.data username = form.username.data password = form.password.data deposit = form.deposit.data user = User(email=email, username=username, password=password, deposit=deposit) db.session.add(user) db.session.commit() return "注册成功" else: return f"注册失败,{form.errors}" app.add_url_rule("/register", view_func=RegisterView.as_view("register")) # 登录 class LoginView(views.MethodView): def get(self): return render_template("login.html") def post(self): form = LoginForm(request.form) if form.validate(): email = form.email.data password = form.password.data user = User.query.filter(User.email == email, User.password == password).first() if user: session["session_id"] = user.id return "登录成功" else: return "邮箱或密码错误" else: return f"{form.errors}" app.add_url_rule("/login", view_func=LoginView.as_view("login")) # 转账 class TransferView(views.MethodView): def get(self): # 只有登录了才能转账,否则让其滚回登录页面 if session.get("session_id"): return render_template("transfer.html") else: return redirect(url_for("login")) def post(self): form = TransferForm(request.form) if form.validate(): email = form.email.data money = form.money.data user = User.query.filter_by(email=email).first() if user: session_id = session.get("session_id") myself = User.query.get(session_id) if myself.deposit >= money: user.deposit += money myself.deposit -= money db.session.commit() return f"转账成功,您向{user.email}转了{money}" else: return "您的资金不足,无法完成当前转账" else: return "该用户不存在" else: return "数据填写不正确" app.add_url_rule("/transfer", view_func=TransferView.as_view("transfer")) if __name__ == '__main__': app.run()
16.实战项目-病毒网站使用CSRF漏洞转账
pass
17.CSRF防御原理
pass
18.flask中CSRF的防御方法与原理
pass
19.Local线程隔离对象
import threading name = "satori" class MyThread(threading.Thread): def run(self): global name name = "koishi" print("子线程:", name) # 子线程: koishi mt = MyThread() mt.start() mt.join() print("主线程:", name) # 主线程: koishi ''' 可以看到打印的name都是koishi,因为子线程和主线程共享同一份数据 '''
import threading from werkzeug.local import Local local = Local() local.name = "satori" class MyThread(threading.Thread): def run(self): local.name = "koishi" print("子线程:", local.name) # 子线程: koishi mt = MyThread() mt.start() mt.join() print("主线程:", local.name) # 主线程: satori ''' local可以自动隔离,打印的是不同的数据。绑定在local上的数据,线程之间都是隔离的。python的threading也有local,功能类似,但是没有werkzeug里的强大 '''
20.app上下文和request上下文
pass
21.线程隔离的g对象
b.py
from flask import g print(g.name)
from flask import Flask, g app = Flask(__name__) @app.route('/') def hello_world(): g.name = "satori" import b return 'Hello World!' if __name__ == '__main__': app.run()
22.before_request钩子函数
from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello World!' # 加上这个装饰器之后,当flask项目上线之后,第一次请求的时候会执行这个函数 @app.before_first_request def first_request(): print("第一次请求的时候执行") # 加上这个装饰器之后,当flask项目上线之后,每一次请求的时候都会执行这个函数 @app.before_request def request(): print("每一次请求都会执行") if __name__ == '__main__': app.run()
23.context_processor钩子函数
from flask import Flask, render_template app = Flask(__name__) ''' 比方说,我登陆一个页面,只要我登陆了就应该显示我的信息,不管我在那个页面 但是这样,每一个视图都要像这样 ''' @app.route("/index") def index(): return render_template("index.html", user="satori") @app.route("/blog") def blog(): return render_template("blog.html", user="satori") if __name__ == '__main__': app.run()
from flask import Flask, render_template app = Flask(__name__) @app.route("/index") def index(): return render_template("index.html") @app.route("/blog") def blog(): return render_template("blog.html") # 这个装饰器就保证了我们在渲染模板的时候不用再传入user="satori"了 @app.context_processor def context_processor(): return {"user": "satori"} if __name__ == '__main__': app.run()
24.errorhandler钩子函数
from flask import Flask, render_template app = Flask(__name__) @app.route("/index") def index(): return "hello world" # 在出错的时候,不会出现那种英文提示。而是我们自定制的错误提示,当然也可以渲染一个木板,或者重定向到其他页面 @app.errorhandler(404) def errorhandler(): return "页面找不到啦" if __name__ == '__main__': app.run()
from flask import Flask, render_template, redirect, abort app = Flask(__name__) @app.route("/index") def index(): return "hello world" @app.route("/koishi") def koishi(): # 比方说这个连接已经不存在了,那么我们可以手动抛出一个错误 # 然后会自动被捕获到 abort(500) @app.errorhandler(500) def server_error(error): return "服务器内部错误,koishi路由被移除了" if __name__ == '__main__': app.run()
25.信号机制以及使用场景
from blinker import Namespace # 1.定义信号 namespace = Namespace() fire_signal = namespace.signal("fire") # 2.监听信号 # 首先定义一个回调函数 def open_fire(sender): print("open fire·····") # 监听信号,发生则调用回调函数 fire_signal.connect(open_fire) # 3.发送一个信号 fire_signal.send() ''' 步骤就三步:定义信号,监听信号,发送信号 '''
from flask import Flask, request from blinker import Namespace app = Flask(__name__) # 定义一个登录的信号,以后用户登录进来以后,就发送一个登录信号,然后能够监听这个这个信号 # 在监听到这个信号以后,就记录当前这个用户登录的信息 # 用信号的方式,记录用户的登录信息 # 1.定义信号 namespace = Namespace() login_signal = namespace.signal("login") # 2.监听信号 def login_log(sender, user_name): print(f"用户{user_name},ip:{request.remote_addr}登录了") login_signal.connect(login_log) @app.route("/login") def login(): username = request.args.get("username") if username: # 3.发送信号 # 这里面也是可以有参数的,user_name会自动传到login_log里的user_name里 login_signal.send(user_name=username) return "登陆成功" else: return "请输入用户名" if __name__ == '__main__': app.run()
26.信号机制以及使用场景
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>欢迎来到避难小屋</h1> </body> </html>
from flask import Flask, request, render_template, template_rendered app = Flask(__name__) def template_render_func(sender, template, context): print(template) # <Template '1.html'> print(context) # {'g': <flask.g of 'app'>, 'request': <Request 'http://localhost:5000/' [GET]>, 'session': <NullSession {}>} template_rendered.connect(template_render_func) ''' 当我们渲染模板的时候,会自动传过来template和context ''' @app.route("/") def index(): return render_template("1.html") if __name__ == '__main__': app.run()
template_rendered = _signals.signal('template-rendered') 模板渲染完成后的信号 before_render_template = _signals.signal('before-render-template') 模板渲染前的信号 request_started = _signals.signal('request-started') 模板开始渲染 request_finished = _signals.signal('request-finished') 模板渲染完成 request_tearing_down = _signals.signal('request-tearing-down') request对象被销毁 got_request_exception = _signals.signal('got-request-exception') 视图函数发生异常。一般可以监听这个信号,来记录网站异常信息 appcontext_tearing_down = _signals.signal('appcontext-tearing-down') app上下文被销毁的信号 appcontext_pushed = _signals.signal('appcontext-pushed') app上下文被推入到栈上的信号 appcontext_popped = _signals.signal('appcontext-popped') app上下文被推出到栈上的信号 message_flashed = _signals.signal('message-flashed') 调用了flask的finished方法的信号