Flask wtforms 数据和 pic 文件更新不与 sqlite DB 同步

Flask wtforms data and pic file update not syncing with sqlite DB

到目前为止,用户登录和注册工作正常。一旦用户登录并尝试更新 usernameemailprofile_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)