Flask wtforms 数据和 pic 文件更新不与 sqlite DB 同步
Flask wtforms data and pic file update not syncing with sqlite DB
到目前为止,用户登录和注册工作正常。一旦用户登录并尝试更新 username
、email
和 profile_image
(prj/templates/account.html
),views/db 不会反映更改。
用户个人资料图片更新不会将更新后的图片文件保存在 prj/static/product_pics
下,因此更新后的图片不会反映在 prj/templates/accounts.html
中。
这个我真的很纳闷。我在这里做错了什么?我一直在努力解决这个问题,但没有任何运气。
My Python env
: Python 3.8.5 64 位
Flask==1.0.2
Flask-Login==0.4.1
Flask-WTF==0.14.2
Pillow PIL==9.0.0
SQLAlchemy==1.2.6
WTForms==2.1
Code Structure:
prj
|--static/
| |--product_pics #pic update not working (form.picture.data is not saved somehow)
|--templates/
| |--account.html #upon"update" button, updated username & email dont get saved in DB
|--users/
| |--forms.py #UpdateUserForm(FlaskForm)
| |--views.py #none of username,email,pic file saved into permanant storage y?
|--models.py #user class containing username, email, and user profile pic name
|--data.sqlite
|--__init__.py
models.py
from prj import db,login_manager
from datetime import datetime
from werkzeug.security import generate_password_hash,check_password_hash
from flask_login import UserMixin
@login_manager.user_loader
def load_user(user_id):
return User.query.get(user_id)
class User(db.Model, UserMixin):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key = True)
profile_image = db.Column(db.String(600), nullable=False, default='default_profile.png')
email = db.Column(db.String(64), unique=True, index=True)
username = db.Column(db.String(64), unique=True, index=True)
password_hash = db.Column(db.String(128))
products = db.relationship('Product', backref='author', lazy=True)
def __init__(self, email, username, password):
self.email = email
self.username = username
self.password_hash = generate_password_hash(password)
def check_password(self,password):
return check_password_hash(self.password_hash,password)
def __repr__(self):
return f"UserName: {self.username}"
.
.
.
init.py
import os
from flask import Flask, current_app
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_login import LoginManager
app = Flask(__name__)
app.config['SECRET_KEY'] = 'mysecret'
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0
#app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
### DATABASE SETUPS ############
basedir = os.path.abspath(os.path.dirname(__file__))
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'data.sqlite')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
Migrate(app,db)
#### LOGIN CONFIGS #######
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = "users.login"
from prj.core.views import core
from prj.users.views import users
.
.
.
app.register_blueprint(core)
app.register_blueprint(users)
prj/templates/account.html
<div class="jumbotron">
<div align='center'>
<h1>Welcome to the page for {{current_user.username}}.</h1>
<img align='center' src="{{profile_image}}">
<p>{{ current_user.email }}</p>
</div>
</div>
.
.
.
<form method="POST" action="" enctype="multipart/form-data">
{{ form.hidden_tag() }}
<div class="form-group">
{{form.username.label(class="form-group") }}
{{form.username(class='form-control') }}
</div>
<div class="form-group">
{{ form.email.label(class="form-group") }}
{{form.email(class='form-control') }}
</div>
<!--<div class="form-group">
{{ form.picture.label(class="form-group") }}
{{ form.picture(class="form-control-file")}}
</div>-->
<div>
<label for="picture" class="form-label">Upload your profile Pic</label>
<input class="form-control form-control-lg" id="picture" type="file">
</div>
<div class="form-group">
{{ form.submit(class="btn btn-primary") }}
</div>
</form>
</div>
{% endblock content %}
prj/users/form.py
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField, ValidationError
from wtforms.validators import DataRequired,Email,EqualTo
from flask_wtf.file import FileField, FileAllowed
from werkzeug import secure_filename
from prj.models import User
.
.
.
class UpdateUserForm(FlaskForm):
email = StringField('Email', validators=[DataRequired(),Email()])
username = StringField('Username', validators=[DataRequired()])
picture = FileField('Update Profile Picture', validators=[FileAllowed(['jpg','png'])])
submit = SubmitField('Update')
def validate_email(self, email):
if User.query.filter_by(email=email.data).first():
raise ValidationError('Your email has been registered already!')
def validate_username(self, username):
if User.query.filter_by(username=username.data).first():
raise ValidationError('Sorry, that username is taken!')
prj/users/views.py
import os
from flask import render_template, url_for, flash, redirect, request, Blueprint
from flask_login import login_user, current_user, logout_user, login_required
from prj.models import User, Product
from prj.users.forms import RegistrationForm, LoginForm, UpdateUserForm
from PIL import Image
from prj import db
from flask import current_app
users = Blueprint('users', __name__)
.
.
.
def add_profile_pic(pic_upload,username):
filename = pic_upload.filename
ext_type = filename.split('.')[-1]
storage_filename = str(username) + '.' +ext_type
filepath = os.path.join(current_app.root_path, 'static\product_pics', storage_filename)
output_size = (200, 200)
pic = Image.open(pic_upload)
pic.thumbnail(output_size)
pic.save(filepath)
return storage_filename
@users.route("/account", methods=['GET', 'POST'])
@login_required
def account():
form = UpdateUserForm()
if form.validate_on_submit():
print(form)
username = current_user.username
user_to_update=User.query.filter_by(username).first()
if form.picture.data:
user_to_update.profile_image = add_profile_pic(form.picture.data,username)
user_to_update.username = form.username.data
user_to_update.email = form.email.data
db.session.merge(user_to_update)
db.session.commit()
flash('User Account Updated')
return redirect(url_for('users.account'))
elif request.method == 'GET':
form.username.data = current_user.username
form.email.data = current_user.email
profile_image = url_for('static', filename='product_pics/'+current_user.profile_image)
return render_template('account.html', profile_image=profile_image, form=form)
使用 if 语句单独更新用户 obj 2
下的用户名、电子邮件和图片。还为 account.html
中的 picture
键添加了缺失的 <input name>
if form.username.data and form.username.data != current_user.username:
current_user.username = form.username.data
if form.email.data and form.email.data != current_user.email:
current_user.email = form.email.data
if form.picture.data:
current_user.profile_image = add_profile_pic(form.picture.data,current_user.username)
print(current_user.profile_image)
到目前为止,用户登录和注册工作正常。一旦用户登录并尝试更新 username
、email
和 profile_image
(prj/templates/account.html
),views/db 不会反映更改。
用户个人资料图片更新不会将更新后的图片文件保存在 prj/static/product_pics
下,因此更新后的图片不会反映在 prj/templates/accounts.html
中。
这个我真的很纳闷。我在这里做错了什么?我一直在努力解决这个问题,但没有任何运气。
My Python env
: Python 3.8.5 64 位
Flask==1.0.2
Flask-Login==0.4.1
Flask-WTF==0.14.2
Pillow PIL==9.0.0
SQLAlchemy==1.2.6
WTForms==2.1
Code Structure:
prj
|--static/
| |--product_pics #pic update not working (form.picture.data is not saved somehow)
|--templates/
| |--account.html #upon"update" button, updated username & email dont get saved in DB
|--users/
| |--forms.py #UpdateUserForm(FlaskForm)
| |--views.py #none of username,email,pic file saved into permanant storage y?
|--models.py #user class containing username, email, and user profile pic name
|--data.sqlite
|--__init__.py
models.py
from prj import db,login_manager
from datetime import datetime
from werkzeug.security import generate_password_hash,check_password_hash
from flask_login import UserMixin
@login_manager.user_loader
def load_user(user_id):
return User.query.get(user_id)
class User(db.Model, UserMixin):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key = True)
profile_image = db.Column(db.String(600), nullable=False, default='default_profile.png')
email = db.Column(db.String(64), unique=True, index=True)
username = db.Column(db.String(64), unique=True, index=True)
password_hash = db.Column(db.String(128))
products = db.relationship('Product', backref='author', lazy=True)
def __init__(self, email, username, password):
self.email = email
self.username = username
self.password_hash = generate_password_hash(password)
def check_password(self,password):
return check_password_hash(self.password_hash,password)
def __repr__(self):
return f"UserName: {self.username}"
.
.
.
init.py
import os
from flask import Flask, current_app
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_login import LoginManager
app = Flask(__name__)
app.config['SECRET_KEY'] = 'mysecret'
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0
#app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
### DATABASE SETUPS ############
basedir = os.path.abspath(os.path.dirname(__file__))
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'data.sqlite')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
Migrate(app,db)
#### LOGIN CONFIGS #######
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = "users.login"
from prj.core.views import core
from prj.users.views import users
.
.
.
app.register_blueprint(core)
app.register_blueprint(users)
prj/templates/account.html
<div class="jumbotron">
<div align='center'>
<h1>Welcome to the page for {{current_user.username}}.</h1>
<img align='center' src="{{profile_image}}">
<p>{{ current_user.email }}</p>
</div>
</div>
.
.
.
<form method="POST" action="" enctype="multipart/form-data">
{{ form.hidden_tag() }}
<div class="form-group">
{{form.username.label(class="form-group") }}
{{form.username(class='form-control') }}
</div>
<div class="form-group">
{{ form.email.label(class="form-group") }}
{{form.email(class='form-control') }}
</div>
<!--<div class="form-group">
{{ form.picture.label(class="form-group") }}
{{ form.picture(class="form-control-file")}}
</div>-->
<div>
<label for="picture" class="form-label">Upload your profile Pic</label>
<input class="form-control form-control-lg" id="picture" type="file">
</div>
<div class="form-group">
{{ form.submit(class="btn btn-primary") }}
</div>
</form>
</div>
{% endblock content %}
prj/users/form.py
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField, ValidationError
from wtforms.validators import DataRequired,Email,EqualTo
from flask_wtf.file import FileField, FileAllowed
from werkzeug import secure_filename
from prj.models import User
.
.
.
class UpdateUserForm(FlaskForm):
email = StringField('Email', validators=[DataRequired(),Email()])
username = StringField('Username', validators=[DataRequired()])
picture = FileField('Update Profile Picture', validators=[FileAllowed(['jpg','png'])])
submit = SubmitField('Update')
def validate_email(self, email):
if User.query.filter_by(email=email.data).first():
raise ValidationError('Your email has been registered already!')
def validate_username(self, username):
if User.query.filter_by(username=username.data).first():
raise ValidationError('Sorry, that username is taken!')
prj/users/views.py
import os
from flask import render_template, url_for, flash, redirect, request, Blueprint
from flask_login import login_user, current_user, logout_user, login_required
from prj.models import User, Product
from prj.users.forms import RegistrationForm, LoginForm, UpdateUserForm
from PIL import Image
from prj import db
from flask import current_app
users = Blueprint('users', __name__)
.
.
.
def add_profile_pic(pic_upload,username):
filename = pic_upload.filename
ext_type = filename.split('.')[-1]
storage_filename = str(username) + '.' +ext_type
filepath = os.path.join(current_app.root_path, 'static\product_pics', storage_filename)
output_size = (200, 200)
pic = Image.open(pic_upload)
pic.thumbnail(output_size)
pic.save(filepath)
return storage_filename
@users.route("/account", methods=['GET', 'POST'])
@login_required
def account():
form = UpdateUserForm()
if form.validate_on_submit():
print(form)
username = current_user.username
user_to_update=User.query.filter_by(username).first()
if form.picture.data:
user_to_update.profile_image = add_profile_pic(form.picture.data,username)
user_to_update.username = form.username.data
user_to_update.email = form.email.data
db.session.merge(user_to_update)
db.session.commit()
flash('User Account Updated')
return redirect(url_for('users.account'))
elif request.method == 'GET':
form.username.data = current_user.username
form.email.data = current_user.email
profile_image = url_for('static', filename='product_pics/'+current_user.profile_image)
return render_template('account.html', profile_image=profile_image, form=form)
使用 if 语句单独更新用户 obj 2
下的用户名、电子邮件和图片。还为 account.html
picture
键添加了缺失的 <input name>
if form.username.data and form.username.data != current_user.username: current_user.username = form.username.data if form.email.data and form.email.data != current_user.email: current_user.email = form.email.data if form.picture.data: current_user.profile_image = add_profile_pic(form.picture.data,current_user.username) print(current_user.profile_image)