Flask生态(二):Flask-Login、Flask-Migrate、Flask-WTF、Flask-Mail

发布时间:2026/9/1 20:17:58
Flask生态(二):Flask-Login、Flask-Migrate、Flask-WTF、Flask-Mail 前文Flask生态一Flask-RESTful、Flask-Admin、Flask-SocketIO、Flask-SQLAlchemy、Flask-MonitoringDashboard的后续继续介绍几款围绕着Flask的开源项目。Flask-Login开源GitHub3.7K Star809 Fork官方文档。功能定位用户会话管理登录/登出/记住我。原理通过Flask的Session机制存储用户ID利用login_user()和logout_user()管理登录状态login_required装饰器保护需要登录的路由Session保护机制通过IPUser-Agent生成标识符检测会话劫持支持basic标记非新鲜和strong删除会话两种模式记住我功能通过持久化Cookie Token实现优势极简API与数据库无关可配合任何用户模型劣势只处理登录/未登录状态不处理权限管理需配合Flask-Principal或自建权限系统实战基于pip安装pip install Flask-Login入门示例importflaskimportflask_login appflask.Flask(__name__)app.secret_keysuper secret stringlogin_managerflask_login.LoginManager()login_manager.init_app(app)classUser(flask_login.UserMixin):def__init__(self,email,password):self.idemail self.passwordpassword users{leafstorm:User(leafstorm,secret)}login_manager.user_loaderdefuser_loader(id):returnusers.get(id)app.get(/login)deflogin():returnform methodpost Email: input nameemailbr Password: input namepassword typepasswordbr buttonLog In/button /formapp.post(/login)deflogin():userusers.get(flask.request.form[email])ifuserisNoneoruser.password!flask.request.form[password]:returnflask.redirect(flask.url_for(login))flask_login.login_user(user)returnflask.redirect(flask.url_for(protected))app.route(/protected)flask_login.login_requireddefprotected():returnflask.render_template_string(Logged in as: {{ user.id }},userflask_login.current_user)app.route(/logout)deflogout():flask_login.logout_user()returnLogged outFlask-Migrate开源GitHub2.4K Star235 Fork项目基于Alembic的数据库迁移管理与Flask-SQLAlchemy配合使用官方文档。原理封装Alembic的迁移引擎通过flask db init、flask db migrate、flask db upgrade等命令管理数据库Schema变更。Alembic会自动检测模型与数据库的差异生成迁移脚本。迁移三步步骤命令作用对标Django初始化flask db init创建migrations/目录类似于.git仅执行一次—生成flask db migrate -m 描述检测模型变化生成迁移脚本makemigrations执行flask db upgrade将迁移脚本应用到数据库migrateFlask-Migrate和Alembic的主要区别在于集成和使用方式Alembic独立工具可用于任何Python项目提供更底层的数据库迁移功能允许用户直接编写SQL迁移脚本。Flask-Migrate为Flask应用提供更高级别的接口自动检测模型更改并生成相应的迁移脚本这使得在Flask应用中进行数据库迁移更加简单和自动化。优势与Flask-SQLAlchemy、Flask CLI深度集成自动化程度高劣势迁移脚本的自动生成在复杂场景下可能不准确需手动调整实战基于pip安装pip install Flask-Migrate设置Alembic环境在项目中创建Alembic配置文件通常命名为alembic.ini包含数据库URL和其他配置选项。配置文件则可参考官方提供的Mako模版选择模板时根据是否多库以及异步Flask应用来判断。入门示例fromflaskimportFlaskfromflask_sqlalchemyimportSQLAlchemyfromflask_migrateimportMigrate appFlask(__name__)app.config[SQLALCHEMY_DATABASE_URI]sqlite:///app.dbdbSQLAlchemy(app)migrateMigrate(app,db)classUser(db.Model):iddb.Column(db.Integer,primary_keyTrue)namedb.Column(db.String(128))然后执行上述三条命令。其他命令行示例# 查看当前数据库处于哪个版本flask db current# 查看所有迁移历史flask dbhistory# 回滚迁移flask db downgrade# 合并迁移flask db merge flask db merge 002_add_column1 003_drop_column 004_add_column2注意解读私有化部署等多种场景下应用会累积有多个连续的迁移脚本可使用合并迁移功能将它们合并成一个。有助于简化迁移历史减少迁移脚本的数量使数据库迁移过程更加清晰。注意得是连续产生的脚本。不是所有操作都能自动回滚生产环境回滚前务必备份数据。Flask-WTF开源GitHub1.5K Star316 ForkFlask扩展集成WTForms用于简化Web表单的处理。可轻松定义表单、验证用户输入、给出清晰已读错误提示并内置CSRF跨站请求伪造保护使Flask应用更安全、更易维护。提供直观API让表单编程变得简单高效。官方文档。基于WTForms为Flask封装提供表单类定义字段校验规则集中管理自动CSRF保护模板中渲染字段和错误信息form.validate_on_submit()统一处理GET/POST判断与校验常用字段类型字段类HTML标签说明StringFieldinput typetext单行文本PasswordFieldinput typepassword密码输入框EmailFieldinput typeemail邮箱输入框TextAreaFieldtextarea多行文本BooleanFieldinput typecheckbox复选框SelectFieldselect下拉选择常用验证器验证器作用DataRequired()字段不能为空Length(min, max)字符长度限制Email()邮箱格式校验EqualTo(field_name)与另一个字段值一致Optional()允许字段为空跳过后续验证器对比项手动request.formFlask-WTF代码行数每个字段重复取/验/报三步表单类定义一次CSRF保护需手动实现自动自定义验证手动if/elsevalidate_xxx方法错误展示手动拼接form.field.errors自动收集适用场景简单表单如搜索框复杂表单注册、登录、资料编辑实战基于pip安装pip install Flask-WTF在app.py中配置SECRET_KEYCSRF Token依赖此密钥app.config[SECRET_KEY]your-secret-key# 生产环境用环境变量app.config[WTF_CSRF_ENABLED]True# 默认开启定义表单类# app/forms.pyfromflask_wtfimportFlaskFormfromwtformsimportStringField,PasswordField,EmailField,BooleanFieldfromwtforms.validatorsimportDataRequired,Length,Email,EqualTo,ValidationErrorfromapp.modelsimportUserclassRegisterForm(FlaskForm):注册表单usernameStringField(用户名,validators[DataRequired(message用户名不能为空),Length(min3,max50,message用户名长度 3-50 个字符)])emailEmailField(邮箱,validators[DataRequired(message邮箱不能为空),Email(message请输入有效的邮箱地址)])passwordPasswordField(密码,validators[DataRequired(message密码不能为空),Length(min6,message密码至少 6 位)])confirm_passwordPasswordField(确认密码,validators[DataRequired(message请再次输入密码),EqualTo(password,message两次输入的密码不一致)])# 自定义验证器检查用户名是否已被注册defvalidate_username(self,field):ifUser.query.filter_by(usernamefield.data).first():raiseValidationError(该用户名已被使用。)defvalidate_email(self,field):ifUser.query.filter_by(emailfield.data).first():raiseValidationError(该邮箱已被注册。)classLoginForm(FlaskForm):登录表单usernameStringField(用户名,validators[DataRequired(message用户名不能为空)])passwordPasswordField(密码,validators[DataRequired(message密码不能为空)])rememberBooleanField(记住我)在视图中使用表单# 文件路径app/blueprints/auth.py重构后的 register 和 loginfromflaskimportBlueprint,render_template,redirect,url_for,flashfromflask_loginimportlogin_user,logout_user,login_required,current_userfromapp.formsimportRegisterForm,LoginFormfromapp.modelsimportUser,db auth_bpBlueprint(auth,__name__)auth_bp.route(/register,methods[GET,POST])defregister():ifcurrent_user.is_authenticated:returnredirect(url_for(main.index))formRegisterForm()# form.validate_on_submit() 在 POST 请求时校验表单# GET 请求或校验失败时返回 Falseifform.validate_on_submit():userUser(usernameform.username.data,emailform.email.data)user.set_password(form.password.data)db.session.add(user)db.session.commit()login_user(user)flash(f注册成功欢迎你{user.username},success)returnredirect(url_for(main.index))# GET请求或校验失败时渲染带表单的模板returnrender_template(register.html,formform)auth_bp.route(/login,methods[GET,POST])deflogin():ifcurrent_user.is_authenticated:returnredirect(url_for(main.index))formLoginForm()ifform.validate_on_submit():userUser.query.filter_by(usernameform.username.data).first()ifuserisNoneornotuser.check_password(form.password.data):flash(用户名或密码错误。,error)returnrender_template(login.html,formform)login_user(user,rememberform.remember.data)flash(f欢迎回来{user.username},success)next_pagerequest.args.get(next)returnredirect(next_pageorurl_for(main.index))returnrender_template(login.html,formform)模板中渲染表单!-- 文件路径app/templates/register.html --{% extends base.html %} {% block title %}注册 - RUNOOB 博客{% endblock %} {% block content %}divclassauth-formh2注册/h2formmethodpost!-- form.hidden_tag() 自动生成 CSRF Token 隐藏字段 --{{ form.hidden_tag() }}divclassform-group{{ form.username.label }} {{ form.username(classform-input) }} {% if form.username.errors %} {% for error in form.username.errors %}spanclassfield-error{{ error }}/span{% endfor %} {% endif %}/divdivclassform-group{{ form.email.label }} {{ form.email(classform-input) }} {% for error in form.email.errors %}spanclassfield-error{{ error }}/span{% endfor %}/divdivclassform-group{{ form.password.label }} {{ form.password(classform-input) }} {% for error in form.password.errors %}spanclassfield-error{{ error }}/span{% endfor %}/divdivclassform-group{{ form.confirm_password.label }} {{ form.confirm_password(classform-input) }} {% for error in form.confirm_password.errors %}spanclassfield-error{{ error }}/span{% endfor %}/div{{ form.submit(classbtn-submit) }}/formpclassform-footer已有账号ahref{{ url_for(auth.login) }}立即登录/a/p/div{% endblock %}Flask-WTF的CSRF保护是自动的form.hidden_tag()会生成一个隐藏的input包含随机Token服务器在接受POST请求前验证此Token。任何外站提交的表单都因为没有正确Token而被拒绝。Flask-MailFlask的开源GitHub642 Star179 Fork官方扩展用于在Flask应用中发送电子邮件。封装SMTP和其他邮件发送协议简化邮件发送过程支持多种邮件后端。可轻松集成邮件功能到Web应用中如发送用户注册确认、密码重置或通知邮件。官方文档。功能附件发送使用msg.attach方法添加附件批量发送通过循环或使用邮件列表发送多封邮件使用模板结合Jinja2模板生成动态邮件内容自定义邮件后端支持Sendmail或其他后端。实战使用pip进行安装pip install Flask-Mail入门示例fromflaskimportFlaskfromflask_mailimportMail appFlask(__name__)# 配置SMTP服务器参数app.config[MAIL_SERVER]smtp.gmail.com# SMTP服务器地址app.config[MAIL_PORT]587# 端口Gmail使用587TLS或465SSLapp.config[MAIL_USE_TLS]True# 启用TLS加密更安全如果使用SSL设置MAIL_USE_SSLTrueapp.config[MAIL_USE_SSL]False# Gmail通常用TLS所以设为Falseapp.config[MAIL_USERNAME]admingmail.comapp.config[MAIL_PASSWORD]some-password# 邮箱密码或应用专用密码对于启用两步验证的服务app.config[MAIL_DEFAULT_SENDER]googlegmail.com# 默认发件人可选mailMail(app)注意对于Gmail等邮箱服务如果启用了两步验证不能直接使用普通密码需要生成应用专用密码。此外确保允许“安全性较低的应用程序访问”但出于安全考虑建议使用应用专用密码或OAuth。使用环境变量来存储敏感信息提高安全性importos app.config[MAIL_USERNAME]os.environ.get(MAIL_USERNAME)app.config[MAIL_PASSWORD]os.environ.get(MAIL_PASSWORD)发送普通邮件纯文本邮件适用于简单通知使用Message类创建邮件对象设置收件人、主题和内容。fromflask_mailimportMessage msgMessage(邮件主题,recipients[recipientexample.com])msg.body邮件内容使用Flask-Mail发送。mail.send(msg)HTML邮件允许发送带有格式、链接和图片的内容提供更好用户体验。支持同时发送纯文本和HTML版本确保兼容性。msgMessage(HTML邮件示例,recipients[recipientexample.com])msg.body这是纯文本版本用于不支持HTML的邮件客户端。msg.htmlh1Hello!/h1p这是一封strongHTML邮件/strong您可以添加样式和链接例如a hrefhttps://example.com访问网站/a。/pmail.send(msg)邮件发送可能失败原因包括网络问题、SMTP服务器错误或配置问题。为了提高应用的健壮性需要处理异常。使用try-except块捕获错误并采取相应措施。为了更健壮可考虑使用异步任务队列如Celery发送邮件避免阻塞Web请求或使用重试机制。发送带附件的邮件fromflask_mailimportMessageimportos msgMessage(带附件的邮件,recipients[recipientexample.com])msg.body请查收附件。# 添加附件withopen(path/to/file.pdf,rb)asf:msg.attach(file.pdf,application/pdf,f.read())mail.send(msg)推荐阅读Flask中文教程