Flask+SQLite用户资料页面无法更新数据库问题求助
Flask用户资料编辑功能修复方案
问题核心分析
- 编辑表单未同步用户数据:
edit_account函数仅处理头像字段,未将表单其他修改字段同步到current_user对象,且db.session.commit()错误传入参数,导致数据库无更新。 - 路由扩展性不足:当前
/profile路由仅支持展示登录用户信息,无法满足后续查看其他用户资料的需求。 - 密码字段处理错误:GET请求时将哈希后的密码赋值给表单,提交时未重新生成哈希,且未判断密码是否为空。
分步修复方案
一、修复edit_account函数逻辑
修改views.py中的edit_account函数,完善数据更新流程:
- 移除GET请求中多余的
db.session.commit()(仅填充表单无需提交数据库)。 - 提交表单时,将所有字段赋值给
current_user对象。 - 单独处理密码:仅当用户输入新密码时,重新生成哈希值存入数据库。
- 修正
db.session.commit()调用方式(无需传入参数)。 - 导入
User模型,解决未定义错误。
二、优化资料页路由(支持扩展)
将/profile改为动态路由,同时保留默认跳转登录用户资料页的逻辑:
- 新增
/profile/<int:user_id>动态路由,用于查看指定用户资料。 - 原
/profile路由重定向到当前登录用户的资料页。
修改后的完整代码
Views.py
from models import User from flask import request, redirect, url_for, flash, render_template from flask_login import login_required, current_user from forms import UpdateUserForm from app import db import os from werkzeug.utils import secure_filename from werkzeug.security import generate_password_hash # 实现头像上传保存逻辑 def add_profile_pic(pic_upload, username): filename = secure_filename(pic_upload.filename) save_path = os.path.join(os.getcwd(), 'static', 'profile_pics', f'{username}_{filename}') pic_upload.save(save_path) return f'{username}_{filename}' @users.route('/edit_account', methods=['GET', 'POST']) @login_required def edit_account(): form = UpdateUserForm() if form.validate_on_submit(): # 更新基础信息字段 current_user.username = form.username.data current_user.email = form.email.data current_user.fname = form.fname.data current_user.lname = form.lname.data current_user.phone = form.phone.data current_user.address = form.address.data current_user.postcode = form.postcode.data # 仅当输入新密码时更新哈希值 if form.password.data: current_user.password_hash = generate_password_hash(form.password.data) # 处理头像上传 if form.picture.data: pic = add_profile_pic(form.picture.data, current_user.username) current_user.profile_image = pic # 提交数据库修改 db.session.commit() flash('你的账户已更新') return redirect(url_for('users.profile', user_id=current_user.id)) elif request.method == 'GET': # 填充表单现有数据(密码字段不显示哈希值) form.username.data = current_user.username form.email.data = current_user.email form.fname.data = current_user.fname form.lname.data = current_user.lname form.phone.data = current_user.phone form.address.data = current_user.address form.postcode.data = current_user.postcode profile_image = url_for('static', filename='profile_pics/'+current_user.profile_image) return render_template('edit_account.html', profile_image=profile_image, form=form) # 动态路由:查看指定用户资料 @users.route('/profile/<int:user_id>') @login_required def profile(user_id): user = User.query.get_or_404(user_id) return render_template('profile.html', user=user) # 默认路由:跳转当前登录用户资料页 @users.route('/profile') @login_required def default_profile(): return redirect(url_for('users.profile', user_id=current_user.id))
Profile.html(适配动态路由)
将模板中current_user改为传入的user变量,仅登录用户能看到自己的编辑按钮:
{% extends "base.html" %} {% block content %} <div class="container emp-profile"> <form method="post"> <div class="row"> <div class="col-md-4"> <div class="profile-img"> <img src="{{ url_for('static', filename='profile_pics/'+user.profile_image )}}" alt="Profile Picture"> </div> </div> <div class="col-md-6"> <div class="profile-head"> <h5>Meet {{ user.username }}</h5> <ul class="nav nav-tabs" id="myTab" role="tablist"> <li class="nav-item" role="presentation"> <button class="nav-link active" id="home-tab" data-bs-toggle="tab" data-bs-target="#home-tab-pane" type="button" role="tab" aria-controls="home-tab-pane" aria-selected="true">About</button> </li> <li class="nav-item" role="presentation"> <button class="nav-link" id="profile-tab" data-bs-toggle="tab" data-bs-target="#profile-tab-pane" type="button" role="tab" aria-controls="profile-tab-pane" aria-selected="false">Timeline</button> </li> </ul> </div> </div> <div class="col-md-2"> {% if current_user.id == user.id %} <a class="btn btn-primary" href="{{ url_for('users.edit_account') }}" class="profile-edit-btn" name="btnAddMore">Edit Profile</a> {% endif %} </div> </div> <div class="row"> <div class="col-md-4"> <div class="profile-work"> <p>ABOUT</p> <h6>Name: {{ user.fname }}</h6><br/> <a href="">Bootsnipp Profile</a><br/> <a href="">Bootply Profile</a> <p>SKILLS</p> <a href="">Web Designer</a><br/> <a href="">Web Developer</a><br/> <a href="">WordPress</a><br/> <a href="">WooCommerce</a><br/> <a href="">PHP, .Net</a><br/> </div> </div> <div class="col-md-8"> <div class="tab-content profile-tab" id="myTabContent"> <div class="tab-pane fade show active" id="home-tab-pane" role="tabpanel" aria-labelledby="home-tab"> <div class="row"> <div class="col-md-6"> <label>Username</label> </div> <div class="col-md-6"> <p>{{ user.username }}</p> </div> </div> <div class="row"> <div class="col-md-6"> <label>Name</label> </div> <div class="col-md-6"> <p>{{ user.fname }} {{ user.lname }}</p> </div> </div> <div class="row"> <div class="col-md-6"> <label>Email</label> </div> <div class="col-md-6"> <p>{{ user.email }}</p> </div> </div> <div class="row"> <div class="col-md-6"> <label>Phone</label> </div> <div class="col-md-6"> <p>{{ user.phone }}</p> </div> </div> <div class="row"> <div class="col-md-6"> <label>Profession</label> </div> <div class="col-md-6"> <p>Driving Instructor</p> </div> </div> </div> <div class="tab-pane fade" id="profile-tab-pane" role="tabpanel" aria-labelledby="profile-tab"> <div class="row"> <div class="col-md-6"> <label>Teaches</label> </div> <div class="col-md-6"> <p>Manual Gearbox</p> </div> </div> <div class="row"> <div class="col-md-6"> <label>Hourly Rate</label> </div> <div class="col-md-6"> <p>£40/hr</p> </div> </div> <div class="row"> <div class="col-md-6"> <label>Car</label> </div> <div class="col-md-6"> <p>Audi A1 TFSI Sportback 2019</p> </div> </div> <div class="row"> <div class="col-md-6"> <label>Years of experience</label> </div> <div class="col-md-6"> <p>1yr</p> </div> </div> <div class="row"> <div class="col-md-6"> <label>Availability</label> </div> <div class="col-md-6"> <p>6 months</p> </div> </div> <div class="row"> <div class="col-md-12"> <label>Your Bio</label><br/> <p>Your detail description</p> </div> </div> </div> </div> </div> </div> </form> </div> {% endblock %}
Forms.py(添加唯一性验证)
避免用户名和邮箱重复提交冲突:
from flask_wtf import FlaskForm from wtforms import StringField, PasswordField, FileField, SubmitField from wtforms.validators import Email, DataRequired, ValidationError from models import User from flask_login import current_user class UpdateUserForm(FlaskForm): email = StringField('Email', validators=[Email(), DataRequired()]) password = PasswordField('Password', validators=[]) username = StringField('Username', validators=[DataRequired()]) picture = FileField('Update profile picture', validators=[FileAllowed(['jpg','png'])]) fname = StringField('First name', validators=[]) lname = StringField('Last name', validators=[]) phone = StringField('Phone', validators=[]) address = StringField('Address(excluding postcode)', validators=[]) postcode = StringField('Postcode', validators=[]) submit = SubmitField('Confirm Update') def validate_username(self, username): if username.data != current_user.username: if User.query.filter_by(username=username.data).first(): raise ValidationError('该用户名已被使用,请选择其他用户名。') def validate_email(self, email): if email.data != current_user.email: if User.query.filter_by(email=email.data).first(): raise ValidationError('该邮箱已被注册,请使用其他邮箱。')
额外说明
- 确保
static/profile_pics目录存在并具备写入权限,否则头像上传会失败。 - 密码字段为空时,不会修改数据库中的密码哈希值,保留原密码。
- 动态路由支持后续扩展查看其他用户资料,同时兼容原有登录用户的访问逻辑。
内容的提问来源于stack exchange,提问作者andyhughes73
相关产品推荐
相关产品推荐

