You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flask-Security无法更新用户信息,报错User无save方法

Flask-Security + SQLAlchemy 用户资料更新失败:AttributeError: 'User' object has no attribute 'save'

我用Flask-Security实现了小型Web应用的认证功能,登录、登出等基础功能正常,但开发个人资料页面时无法更新用户记录,报错AttributeError: 'User' object has no attribute 'save'。希望采用兼容Flask-Security和SQLAlchemy的ORM方式解决,不想直接操作session。

视图代码

@profile_bp.route('/', methods=('GET','POST'))
@login_required
def profile():
    

    form_profile = ProfileForm(request.form)
    
    user = User.query.filter_by(id=current_user.id).first()

    if request.method == 'POST' and form_profile.save:
        print("Profile form submitted")
        user = User(
                    updated = utc_now_int(),
                    first_name = form_profile.first_name.data,
                    last_name = form_profile.last_name.data,
                    gender = form_profile.gender.data,
                    date_of_birth = form_profile.date_of_birth.data,
                    )        

        user.save()

        return render_template('profile/profile_saved.html')

    # Populate the form when when user loads his profile and then render the template
    if user:
        form_profile.first_name.data = user.first_name
        form_profile.last_name.data = user.last_name
        form_profile.gender.data = user.gender
        form_profile.date_of_birth.data = user.date_of_birth
    return render_template('profile/userprofile.html', form_profile=form_profile)

模型代码

from app.database import Base
from flask_security.models import fsqla_v3 as fsqla
from flask_security import UserMixin, RoleMixin, AsaList
from sqlalchemy.orm import relationship, backref
from sqlalchemy.ext.mutable import MutableList
from sqlalchemy import Boolean, DateTime, Column, Integer, \
                    String, ForeignKey

from app.utilities.common import utc_now as now, uid_generator

class RolesUsers(Base):
    __tablename__ = 'roles_users'
    id = Column(Integer(), primary_key=True)
    user_id = Column('user_id', Integer(), ForeignKey('users.id'))
    role_id = Column('role_id', Integer(), ForeignKey('roles.id'))


class Role(Base, RoleMixin):
    __tablename__ = 'roles'
    id = Column(Integer(), primary_key=True)
    name = Column(String(80), unique=True)
    description = Column(String(255))
    permissions = Column(MutableList.as_mutable(AsaList()), nullable=True)


class User(Base, UserMixin):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    email = Column(String(255), unique=True)
    password = Column(String(255))
    username = Column(String(255), unique=True, nullable=True)
    fs_uniquifier = Column(String(64), unique=True, nullable=False)
    created = Column(DateTime(), default=lambda: now())
    active = Column(Boolean())
    updated = Column(DateTime())
    # confirmed_at = Column(DateTime(), default=lambda: now())
    confirmed_at = Column(DateTime())
    last_login_at = Column(DateTime())
    current_login_at = Column(DateTime())
    last_login_ip = Column(String(40))
    current_login_ip = Column(String(40))
    login_count = Column(Integer)
    first_name = Column(String(255))
    last_name = Column(String(255))
    gender = Column(String(1))
    date_of_birth = Column(DateTime())
    roles = relationship('Role', secondary='roles_users',
                            backref=backref('users', lazy='dynamic'))

    def __str__(self):
        return self.email

错误信息

AttributeError: 'User' object has no attribute 'save'

解决方案

1. 问题根源

  • User模型未定义save()方法,直接调用会报错。
  • POST请求中重新创建了新的User对象,而非更新已查询到的数据库实例,会丢失用户原有核心信息(如email、fs_uniquifier),甚至产生重复用户。

2. 修正视图代码(标准ORM方式)

直接更新查询到的user实例,通过SQLAlchemy的session提交修改,完全兼容Flask-Security:

from app import db  # 导入你的SQLAlchemy实例,根据实际路径调整

@profile_bp.route('/', methods=('GET','POST'))
@login_required
def profile():
    form_profile = ProfileForm(request.form)
    user = User.query.filter_by(id=current_user.id).first()

    if request.method == 'POST' and form_profile.validate():  # 用表单验证确保数据合法
        print("Profile form submitted")
        # 更新已有user实例的字段,而非新建对象
        user.updated = utc_now_int()
        user.first_name = form_profile.first_name.data
        user.last_name = form_profile.last_name.data
        user.gender = form_profile.gender.data
        user.date_of_birth = form_profile.date_of_birth.data
        
        db.session.commit()  # 提交session保存修改

        return render_template('profile/profile_saved.html')

    # 填充表单初始数据
    if user:
        form_profile.first_name.data = user.first_name
        form_profile.last_name.data = user.last_name
        form_profile.gender.data = user.gender
        form_profile.date_of_birth.data = user.date_of_birth
    return render_template('profile/userprofile.html', form_profile=form_profile)

3. 可选:给User模型添加save方法

如果想保留user.save()的调用方式,可在User模型中添加通用save方法:

class User(Base, UserMixin):
    # ... 原有字段和方法 ...

    def save(self):
        from app import db  # 或在模型顶部提前导入db
        db.session.add(self)
        db.session.commit()

添加后,视图中可使用user.save()替代db.session.commit(),本质仍是通过session提交,完全兼容Flask-Security。

4. 额外注意

  • 表单验证:优先使用form_profile.validate()判断表单提交合法性,避免非法数据入库。
  • 类型匹配:确保utc_now_int()返回的类型与User模型中updated字段(DateTime类型)一致,若返回时间戳需转换为datetime对象。

内容的提问来源于stack exchange,提问作者afsar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 23:48:09