栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

Flask狗书学习笔记(下)

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Flask狗书学习笔记(下)

三.Web表单
Flask-WTF对form进行封装,方便我们使用form表单
1.配置
Flask-WTF要求app设置一个密钥,防止CSRF跨域攻击
app.config["SECRET_KEY"] = "Titans"
//注:密钥最好不要写在源码里面
2.一个简单的实例
demo.py
from flask_wtf import FlaskForm
from wtforms import StringField,SubmitField
from wtforms.validators import DataRequired
from flask import Flask,render_template,request
class NameForm(FlaskForm):
    name = StringField("what is your name",validators=[DataRequired()])
    submit = SubmitField("Submit")
app = Flask(__name__)
app.config['SECRET_KEY'] = "Titans"
@app.route('/',methods=['GET','POST'])
def index():
    name = None     # 最开始访问index,name应该不存在
    form = NameForm()   # form的实例化
    if request.method == 'POST':
        if form.validate_on_submit():
            name = form.name.data   # 获取表单数据
            form.name.data = ''     # 重置表单数据
    return render_template('my_form.html',name=name,form=form)
if __name__ == '__main__':
    app.run()


my_form.html
{% extends "base.html" %}
{% block title %} My_form {% endblock %}
{% block body %}
    Hello,{% if name %}{{ name }}
        {% else %}stranger{% endif %}
    
    
{# 防止CSRF攻击 #} {{ form.hidden_tag() }} {# form中的name字段 #} {{ form.name.label }} {{ form.name(id='my-test-field') }} {# form的提交按钮 #} {{ form.submit() }}
{% endblock %} 总体思想:先创建一个表单类,继承自FlaskForm 然后编写属性,即对应表单的字段 之后在需要使用的时候进行表单实例化即可. 将实例对象传给模板,模板中就可以调用form的方法和属性生成表单. 然后当表单提交给后端时,后端检测到表单做出提交,做出响应即可. 同时如上可以给像html给每个字段指定id,class,attribute,name 3.表单类 在前面看到了一个表单类,这边详细说说如何编写它 class NameForm(FlaskForm): name = StringField("what is your name",validators=[DataRequired()]) submit = SubmitField("Submit") 首先表单继承FlaskForm,然后编写对应字段,上面使用了StringField. 这边讲讲几种常用的字段: BooleanField 复选框,值为True和False FileField 文件上传字段 HiddenField 隐藏的文本字段 MultipleFileField 多文件上传 FloatField 浮点数 PasswordField 密码文本字段 RadioField 一组单选按钮 SelectField 下拉列表 SubmitField 表单提交按钮 StringField 文本字段 TextAreaField 多行文本字段 这边举一个图片上传的实例: 表单类加上file = FileField("上传文件") 当表单提交后执行: file = request.files.get('file') img_path = './static/' + "pic.png" img = Image.open(file) img.save(img_path) 前面看到DataRequired() 这边讲讲常用的validators DataRequired 确保转换类型后字段中有数据 Email 验证电子邮件地址 EqualTo 比较两个字段的值,常用于要求输入两次密码进行确认的情况 Length 验证输入字符串长度 NumberRange 验证输入的值在数字范围之内 Optional 允许字段中没有输入,将跳过其它验证函数 flask-bootstrap和flask-wtf提供了一个快速渲染表单的函数, 使用前需要实例化Bootstrap. {{ wtf.quick_form(form) }} {% extends "base.html" %} {% import "bootstrap/wtf.html" as wtf %} {% block title %} My_form {% endblock %} {% block body %} {{ wtf.quick_form(form) }} {% endblock %} 4.在视图函数中处理表单 在上面完整实例中,index一共干了两件事情 一件是渲染表单,另一件是接受表单数据因此在路由设置methods post处理表单数据,get处理渲染表单 在上诉表单中只有数据能被验证函数接受那么validate_on_submit()方法的返回值才为True 5.重定向和用户会话 前面在表单提交会出现一个问题,当表单提交后然后刷新页面,浏览器会警告再次提交页面。 该怎么解决这个问题呢? 重定向可以解决这个问题,重定向相当于重新发送了一个get请求到这个页面,这样就能解决这个问题. 但是重新发送请求的话,表单数据也会随之消失,因此我们需要保存一下提交表单的数据。 这时我们可以将数据存储在用户会话里面,一般情况下会话存储在客户端cookie中,采用前面app设置 的密钥加密前面,如果cookie发送变化,session也就随之消失了.session相当于保存于客户端的一个字典数据 @app.route('/',methods=['GET','POST']) def index(): form = NameForm() # form的实例化 if form.validate_on_submit(): session['name'] = form.name.data return redirect(url_for("index")) # 等价于 return redirect("/") return render_template('my_form.html',name=session.get('name'),form=form) 6.闪现消息(完整代码p42) 当请求结束后,应该让用户知道这个请求的状态 比如登录密码错误时,应该闪现消息来提示用户正确输入密码 后端调用直接flash("密码错误") 前端使用这个闪现消息 {% for message in get_flashed_message() %} {{ message }} {% endfor %} 这边使用了Boostrap渲染 四.数据库 1.SQL数据库: 主键:是每一行数据的唯一标识符 sql数据将数据以某种关系连接到一起,就像一张表一样呈现他们之间的关系 举个例子: 有一个roles的表, 存有id,name两个字段 另一个表users 存有id username password role_id字段 每张表中的每一行代表一个对象 同时两张表可以通过users.role_id和roles.id联系到一起, 这样就形成了一对多的关系. 常用:mysql,sqlite 2.NoSQL数据库: NoSQL数据库常常以集合代替表,文档代替记录 常见:redis 以key-value存储数据(有点像session,但是它是数据库可以持久化保存数据) 一般情况下,NoSQL数据库的效率会高于SQL数据库 3.Flask-SQLAlchemy 管理数据库(ORM映射数据库) 简化了flask操作数据库,避免大量编写原生的SQL语句,同时也有效防止了SQL注入攻击. (1)配置 app.config["SQLALCHEMY_DATABASE_URI"] ='mysql+pymysql://(username):(password)@(host):(port)/(database_name)' app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False db = SQLAlchemy(app) (2)定义模型 定义模型类,需要继承自db.Model class Role(db.Model): __tablename__ = "roles" id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(64),unique=True) def __repr__(self): return f"" __tablename__是给表取的名字存放在数据库中的名称 常用的列类型 详情阅读p49 常用的列选项: primary_key 设为True,表示为主键 unique 设为True,列不允许出现重复的值 index 设为True,为列创建索引,提升查询效率 nullable 设为True,表示允许为空 default 为列定义默认值 flask-sqlalchemy要求每个模型都定义主键,这一列通常取名为id 每个类,最好重写一个魔术方法__repr()__然后返回一个可读的字符串 4.关系 一对多: class Role(db.Model): users = db.relationship("User",backref="role") class User(db.Model): role_id = db.Column(db.Inter,db.ForeignKey("roles.id")) 通过user中的role_id外键建立起两个模型的关系,传给db.ForeignKey()的参数roles.id 表明这列的值是roles表中对应的Id db.relationship()中的第一个参数表示关联User类,backref参数向User模型添加 一个role属性,从而反向关联,这样User的实例就可以直接访问role而不需要role_id 多对多的关系,只需要来一个中间表将一对多串起来. 5.数据库操作: (1)创建表 db.creat_all() (2)插入行 admin_role = Role(name="Admin") user_role = Role(name="User") mod_role = Role(name="Moderator") user_john = User(username="john",role=user_role) user_susan = User(username='susan',role=user_role) user_david = User(username='david',role=user_role) db.session.add_all([admin_role,mod_role,user_role,user_john,user_susan,user_david]) db.session.commit() (3)修改行 admin_role = Role.query.filter(Role.name == "A").first() admin_role.name = 'Admin' db.session.add(admin_role) db.session.commit() (4)删除行 admin_role = Role.query.filter(Role.name == "Administrator").first() db.session.delete(admin_role) db.session.commit() (5)查询行 全部查询: roles = Role.query.all() 这时候我们前面重写的魔术方法就用上了,可以打印出我们想要的字符串 print(roles) 过滤查询: user_role = Role.query.filter(Role.name == "User").first() 两种方式获取users users = user_role.users users = User.query.filter(User.role == user_role).all() print(users) 在查询语句前加上str即可查看原生SQL语句 print(str(Role.query.filter(Role.name == "User"))) all()返回是一个查询结果列表,first()只返回一个结果,没有返回None filter()过滤查询满足我们需求的数据 常用的查询过滤器 filter() filter_by() limit() 使用指定的值限制原查询返回的结果数量,返回一个新的查询 offset() 偏移原查询返回的结果,返回一个新的查询 order_by() 根据指定条件对原查询结果进行排序,返回一个新的查询 group_by() 根据指定条件对原查询结果进行分组,返回一个新的查询 常用查询执行方法 all() 以列表形式返回查询的结果 first() 返回查询的第一个结果,如果未查询到,返回None count() 返回查询结果的数量 paginate() 返回一个paginate对象,包含指定范围内的结果 6.集成Python shell @app.shell_context_processor def make_shell_context(): return dict(db=db,User=User,Role=Role) 7.数据库迁移 from flask_migrate import Migrate migrate = Migrate(app,db) python manager.py db init 初始化迁移仓库(只要执行一次) # 只有第一次创建数据库才需要初始化 python manager.py db migrate -m "XXX注释" python manager.py db upgrade 更新数据库 python manager.py db downgrade 还原前一个脚本对数据库的改动(可能会造成数据缺失) 五.电子邮箱 1.狗书中采用的是google邮箱,考虑到国外网络延迟,所有这边就演示使用QQ邮箱发送邮箱(网易163同理) 首先给app进行配置相应的信息,这边已经整理好了 app.config["MAIL_SERVER"] = "smtp.qq.com" app.config["MAIL_PORT"] = 465 # 设置邮箱端口为465 app.config["MAIL_USE_SSL"] = True # QQ邮箱需要开启SSL app.config["MAIL_USERNAME"] = "you@qq.com" # 填入你的qq号码 app.config["MAIL_PASSWORD"] = "code" # 填入qq邮箱的授权码(不懂可以自行百度搜索) 小提示:敏感信息最好不要写入源码中,从文件中读入,或者从环境导入 app配置好后 进行Mail实例化: mail = Mail(app) 然后就可以在flask中发送邮箱了 @app.route('/email') def email(): """ Message发送邮件信息, sender为发送者邮箱, recipients为接受者邮箱 """ message = Message("邮箱标题",sender=app.config["MAIL_USERNAME"],recipients=["接受邮箱@qq.com"]) message.body = "邮箱内容" # 添加附件 # with app.open_resource("C:Usersfilename.doc") as doc: # message.attach("my_file.doc",'application/octet-stream', doc.read()) send_email(message) return "发送成功" def send_email(msg): with app.app_context(): mail.send(msg) send函数调用了current_app所以需要app.app_context()激活应用上下文 实际开发时,可以将上面email路由抽象化,传递发送邮箱信息,接收者邮箱的参数,增加函数的灵活性. 2.此时已经可以发送邮箱,但是当发送邮箱数量大时,会发现速度有点慢。 这个时候我们会考虑到io堵塞,当上一封邮件未完全发完,而下一份邮件却在等它发送完毕,这样就造成效率的低下, 为了解决这个问题,采用异步发送邮箱 @app.route('/email') def send_email(): email() return "发送完成" def email(): message = Message("邮箱标题",sender=app.config["MAIL_USERNAME"],recipients=["you@qq.com"]) message.body = "邮箱内容" thr = Thread(target=send_async_email,args=[message]) thr.start() return thr def send_async_email(msg): with app.app_context(): mail.send(msg) 六.大型应用的结构 1.项目结构 结构图详见p65 |-flasky |-app/ |-templates/ |-static/ |-main/ |-__init__.py |-errors.py |-forms.py |-views.py |-__init__.py |-email.py |-models.py |-migrations/ |-tests/ |-__init__.py |-test*.py |-venv/ |-requirements.txt |-config.py |-flasky.py 2.配置选项 应用程序通常需要多个配置环境,对应开发,测试,生成环境. 这样才不会相互造成影响 config.py class Config: SECRET_KEY = "" MAIL_SERVER = "" MAIL_PORT = "" SQLALCHEMY_TRACK_MODIFICATIONS = False @staticmethod def init_app(app): pass class DevelopmentConfig(Config): DEBUG = True SQLALCHEMY_DATABASE_URI = "" class TestingConfig(Config): TESTING = True SQLALCHEMY_DATABASE_URI = "" class ProductionConfig(Config): SQLALCHEMY_DATABASE_URI = "" config = { "development" : DevelopmentConfig(), "testing" : TestingConfig(), "production" : ProductionConfig(), "default" : DevelopmentConfig() } 在父类Config包含共有的配置,然后在子类定义不同环境独有的配置,比如 不同环境中使用数据库不同,然后最后config用一个字典封装,方便使用时调用 ,然后还注册一个默认的环境为开发环境 3.应用包 应用宝存放应用的所有代码、模板和静态文件。称为app,即上面的“|-app/” templates和static目录为app的子目录。 同时数据库模型和电子邮件的支持函数也要移到这个包里面 app/models.py , app/email.py (1)应用工厂函数 可以给脚本留出配置应用的时间,还能够创建多个应用实例,方便测试 app/__init__.py from flask import Flask from flask_bootstrap import Bootstrap from flask_mail import Mail from flask_moment import Moment from flask_sqlalchemy import SQLAlchemy from config import config bootstrap = Bootstrap() mail = Mail() moment = Moment() db = SQLAlchemy() # config_name为需要选择配置环境的名称 def creat_app(config_name): app = Flask(__name__) app.config.from_object(config[config_name]) config[config_name].init_app(app) bootstrap.init_app(app) mail.init_app(app) moment.init_app(app) db.init_app(app) # 注册蓝本 return app (2)蓝本实现应用功能 与应用一样,蓝本可以在单个文件中定义,也可以使用更为结构化的方式在包中的多个模块中创建。 我们在应用包创建一个子包 app/main/__init__.py from flask import Blueprint main = Blueprint("main",__name__) from . import views,errors 应用的路由一个保存在app/main/views.py 应用的错误处理保存在app/main/errors.py 但是如何将__init__ views errors关联起来呢? 只需在__init__ 的末尾加上from . import views,errors. 注意这边要是末尾,不然会导致循环导包 小提示from . import 是从当前包 from .. import 是从包的上一层 这时我们需要的是注册蓝本 app/__init__.py def creat_app(config_name): app = Flask(__name__) app.config.from_object(config[config_name]) config[config_name].init_app(app) bootstrap.init_app(app) mail.init_app(app) moment.init_app(app) db.init_app(app) # 注册蓝本 from .main import main as main_blueprint app.register_blueprint(main_blueprint) return app 主蓝本中错误处理程序 app/main/errors.py from flask import render_template from . import main @main.app_errorhandler(404) def page_not_found(e): pass 视图函数 app/main/views.py from . import main @main.route("/index") def index(): pass 注意这边为main.route() 同时此时使用url_for应该使用main.index为端点名 4.应用脚本 顶层flasky.py from app import creat_app,db from app.models import User,Role from flask_migrate import Migrate app = creat_app("default") migrate = Migrate(app,db) @app.shell_context_processor def make_shell_context(): return dict(db=db,User=User,Role=Role) 使用 set FLASK_APP = flasky.py set FLASK_DEBUG = 1 5.需求文件 requirements.txt说明flask使用依赖包,方便其它计算机运行该程序 pip freeze >requirements.txt 即可以自动生成 如果像创建这个虚拟环境的完整副本,需要先创建一个虚拟环境 然后运行pip install -r requirements.txt 6.单元测试 这边还没学过测试,就留着后面说了 7.创建数据库 flask db upgrade 8.运行 flask run
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/950313.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号