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方法的信号

  

  

5.flask知识点补充的更多相关文章

  1. swagger知识点补充

    1. swagger知识点补充 1.1. 概述 在swagger的使用过程中,除了网上常见的例子,还会有很多细节上的东西需要注意和改写,这里我列几点我使用过程中遇到的问题和改进方式 1.2. 知识点 ...

  2. set集合,深浅拷⻉以及部分知识点补充

    set集合,深浅拷⻉以及部分知识点补充内容:1. 基础数据类型补充2. set集合3. 深浅拷⻉主要内容: ⼀. 基础数据类型补充⾸先关于int和str在之前的学习中已经讲了80%以上了. 所以剩下的 ...

  3. Django 知识点补充

    Django 知识点补充 1 Django如何在Model保存前做一定的固定操作,比如写一条日志 (1)利用Django的Model的Signal Dispatcher, 通过django.db.mo ...

  4. 从零开始的全栈工程师——html篇1.8(知识点补充与浏览器兼容性)

    知识点补充 一.浏览器的兼容问题(关于浏览器的兼容问题 有很多大佬已经解释的很清楚了 这个得自己百度去多花点时间去了解 这里咱们只说一下前面的漏点) 浏览器兼容性问题又被称为网页兼容性或网站兼容性问题 ...

  5. python day4 元组/字典/集合类知识点补充

    目录 python day4 元组/字典/集合类知识点补充 1. 元组tuple知识点补充 2. 字典dict的知识点补充 3. 基本数据类型set 4. 三元运算,又叫三目运算 5. 深复制浅复制 ...

  6. python 知识点补充

    python 知识点补充 简明 python 教程 r 或 R 来指定一个 原始(Raw) 字符串 Python 是强(Strongly)面向对象的,因为所有的一切都是对象, 包括数字.字符串与 函数 ...

  7. java基础知识点补充---二维数组

    #java基础知识点补充---二维数组 首先定义一个二维数组 int[][] ns={ {1,2,3,4}, {5,6,7,8}, {9,10,11,12}, {13,14,15,16} }; 实现遍 ...

  8. disruptor笔记之八:知识点补充(终篇)

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  9. Jaeger知识点补充

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

随机推荐

  1. ardupilot_gazebo仿真(二)

    ardupilot_gazebo仿真(二) 标签(空格分隔): 未分类 在模型中添加sensor gezebo官网-sensor部分教程 gezebo官网-基础部分教程 Gazebo plugins ...

  2. BZOJ 4004 JLOI2015 装备购买 高斯消元+线性基

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4004 Description 脸哥最近在玩一款神奇的游戏,这个游戏里有 n 件装备,每件装 ...

  3. UVA 11883 Repairing a Road(最短路径+暴力枚举)

    You live in a small town with R bidirectional roads connecting C crossings and you want to go from c ...

  4. mysql初始(6)

    随着mysql的运用不断加深,一些更复杂点的用法又需要总结起来. 1.将一个表中的数据插入到另一个表中: a.两张表字段相同,并且数据全部插入,命令如下:  INSERT INTO 目标表 SELEC ...

  5. vue.js的特点-1

    1. Vue.js是数据驱动的,无需手动操作DOM. 它通过一些特殊的HTML语法,将DOM和数据绑定起来.一旦你创建了绑定,DOM将和数据保持同步,每当变更了数据,DOM也会相应的更新. 2. MV ...

  6. bootstrap和elementUI真的会冲突

    前两天,做了一个支持markdown的功能: http://www.cnblogs.com/XHappyness/p/8097756.html 后面发现预览效果某些标签需要bootstrap的支持才能 ...

  7. C++编码规范101

    组织和策略问题 第0条 不要拘泥于小节(又名:了解哪些东西不应该标准化) 第1条 在高警告级别干净利落地进行编译 第2条 使用自动构建系统 第3条 使用版本控制系统 第4条 在代码审查上投入 设计风格 ...

  8. EF 4.0 升级到 6.0 问题解决办法

    1.工具->库程序包管理器-> 管理解决方案的Nuget 程序包  找到EntityFramework 管理,勾选把需要进入 EF6.0的 项目,进行升级.

  9. 【bzoj1018】[SHOI2008]堵塞的交通traffic 线段树区间合并+STL-set

    题目描述 给出一张2*n的网格图,初始每条边都是不连通的.多次改变一条边的连通性或询问两个点是否连通. 输入 第一行只有一个整数C,表示网格的列数.接下来若干行,每行为一条交通信息,以单独的一行“Ex ...

  10. Android开发工具常用快捷键大全

    Android开发中常用的开发工具有android studio和eclipse两种,下面小编整理了一些这两种开发工具中常用的快捷键,使用这些快捷键,你的android编程将事半功倍. android ...