Flask:08-一局王者建立自己的部落格(02)
部落格專案
上一篇內容完善
-
自定義欄位驗證函式
class RegisterForm(FlaskForm): ... def validate_username(self, field): user = User.query.filter(User.username == field.data).first() if user: raise ValidationError('該使用者名稱已註冊,請選用其他名稱註冊') def validate_email(self, field): user = User.query.filter(User.email == field.data).first() if user: raise ValidationError('該郵箱已註冊,請選用其他郵箱註冊')
-
加密儲存密碼
from werkzeug.security import generate_password_hash, check_password_hash class User(db.Model): ... @property def password(self): raise AttributeError('你瞅啥?!密碼不可讀') @password.setter def password(self, password): # 加密儲存密碼 self.password_hash = generate_password_hash(password) # 密碼校驗,True:校驗成功,False:校驗失敗 def verify_password(self, password): return check_password_hash(self.password_hash, password)
使用者登入退出
-
登入校驗邏輯
@user.route('/login/', methods=['GET', 'POST']) def login(): form = LoginForm() if form.validate_on_submit(): u = User.query.filter(User.username == form.username.data).first() if not u: flash('無效的使用者名稱') elif not u.confirmed: flash('賬戶尚未啟用,請啟用後再登入') elif not u.verify_password(form.password.data): flash('無效的密碼') else: flash('登入成功') return redirect(url_for('main.index')) return render_template('user/login.html', form=form)
-
flask-login擴充套件
pip install flask-login
# 第一步:新增擴充套件 from flask_login import LoginManager login_manager = LoginManager() def init_extensions(app): ... login_manager.init_app(app) # 指定登入端點 login_manager.login_view = 'user.login' # 設定提示資訊 login_manager.login_message = '登入後才可訪問' # 第二步:讓模型類類基礎子UserMixin類,以便擁有狀態相關方法 from flask_login import UserMixin class User(UserMixin, db.Model): ... # 第三步:實現回撥函式(根據使用者id返回使用者物件) @login_manager.user_loader def load_user(uid): return User.query.get(uid)
- 總結
狀態切換: login_user# 還可以完成記住我的功能(時間也可以指定) logout_user 狀態查詢: is_authenticated# 登入狀態 is_anonymous# 匿名狀態 路由保護: login_required# 保護需要登入才可訪問的路由 當前使用者: current_user# 在模板中使用不需要分配
使用者資訊管理
-
詳情資訊展示
- 新增點選跳轉連結及邏輯
- 書寫詳情頁面的展示效果
-
修改密碼
- 新增點選跳轉連結
- 模板檔案中新增表單(原始密碼、新密碼、確認密碼)
- 新增點選提交校驗的邏輯
#修改密碼 @user.route('/modifypassword/',methods=['GET','POST']) def modify_password(): form = ModifyPasswordForm() if form.validate_on_submit(): if current_user.verify_password(form.password.data): current_user.password = form.password_new1.data flash('修改密碼成功') return redirect(url_for('user.login')) else: flash('原始密碼錯誤') return render_template('user/modify_password.html', form=form)
-
修改郵箱
- 新增點選跳轉連結
- 模板檔案中新增表單(新郵箱)
- 新增點選提交校驗的邏輯(向新的郵箱地址傳送確認郵件,需要攜帶使用者資訊及新郵箱地址)
- 新增郵箱修改確認的校驗檢視函式(解析攜帶資料,修改使用者郵箱)
#修改郵箱 @user.route('/modifyemail/',methods=['GET','POST']) def modify_email(): form = ModifyEmailForm() if form.validate_on_submit(): if current_user.email == form.email.data: s = Serializer(current_app.config['SECRET_KEY'],expires_in=3600) token = s.dumps({'id':current_user.id,'email':form.email_new.data}) send_mail('郵箱修改',form.email_new.data,'email/modify_email_activate.html',username=current_user.username,token=token) flash('請前往新郵箱確認修改') return redirect(url_for('user.profile')) else: flash('原始郵箱不正確') return render_template('user/modify_email.html',form=form)
-
找回密碼
- 在登入頁面新增找回密碼的跳轉連結
- 跳轉的模板檔案中新增表單(使用者名稱/郵箱地址),提交改為下一步
- 新增提交的校驗邏輯(向用戶的郵箱傳送郵件,需要攜帶使用者身份資訊)
- 新增重新設定密碼的檢視函式(給出再次設定密碼的表單,並處理提交)
# 找回密碼 @user.route('/findpassword/<way>/', methods=['GET', 'POST']) def find_password(way): form = FindPasswordForm() s = Serializer(current_app.config['SECRET_KEY'], expires_in=3600) if form.validate_on_submit(): u = User.query.filter( or_(User.username == form.username_email.data, User.email == form.username_email.data)).first() if u: token = s.dumps({'id': u.id}) # send_mail('通過使用者名稱找回密碼',u.email,'email/find_password_activate.html',username=u.username,token=token) flash('請前往郵箱確認找回密碼') else: flash('使用者名稱錯誤') return redirect(url_for('user.login')) return render_template('user/find_password.html', form=form) @user.route('/findpasswordactivate/<token>/', methods=['GET', 'POST']) def find_password_activate(token): form = NewPasswordForm() s = Serializer(current_app.config['SECRET_KEY']) try: data = s.loads(token) except SignatureExpired as e: flash('token已過期,找回密碼失敗') return redirect(url_for('user.login')) except BadSignature as e: flash('token有誤,找回密碼失敗') return redirect(url_for('user.login')) u = User.query.get(data['id']) if form.validate_on_submit(): u.password = form.password2.data db.session.add(u) flash('找回密碼成功') return redirect(url_for('user.login')) return render_template('user/new_password.html', form=form)
-
修改頭像
- 新增點選跳轉的連結
- 新增flask-uploads擴充套件
- 新增上傳檔案表單及校驗邏輯
- 新增上傳檔案的處理(生成隨機檔名、生成縮圖、展示上傳檔案)
- 將頭像檔名儲存到資料庫(User模型需要新增欄位)
- 練習:使用者詳情頁面展示使用者頭像
# 修改頭像 @user.route('/icon/', methods=['GET', 'POST']) def icon(): form = UploadForm() if form.validate_on_submit(): # 提取上傳檔案資訊 photo = form.photo.data # 提取檔案字尾帶點 suffix = os.path.splitext(photo.filename)[1] # 生成隨機檔名 filename = random_string() + suffix # 用上傳物件儲存圖片,並指定名字 photos.save(photo, name=filename) # 拼接完整的圖片路徑 pathname = os.path.join(current_app.config['UPLOADED_PHOTOS_DEST'], filename) # 縮圖操作,開啟檔案 img = Image.open(pathname) # 設定圖片尺寸 img.thumbnail((32, 32)) # 重新儲存,指定完整路徑 img.save(pathname) # 如果當前使用者用的是預設頭像,則不用刪除 # 若果用的不是預設頭像,為減少儲存需要先刪除原來的頭像 if current_user.icon != 'default.jpg': # os.remove刪除專案本地檔案 os.remove(os.path.join(current_app.config['UPLOADED_PHOTOS_DEST'], current_user.icon)) # 新的頭像準備好了,新增到model裡 current_user.icon = filename # 手動儲存,可有可無 db.session.add(current_user) # 頭像已經儲存到了static和model中,為了展示頭像,可以把圖片的路徑傳給模板 # 靜態檔案是固定格式注:不能直接寫圖片名,那樣就寫死了 # 不同使用者有獨自的頭像,所以用current_user.icon進行拼接 img_url = url_for('static', filename='upload/' + current_user.icon) return render_template('user/icon.html', form=form, img_url=img_url)
部落格管理
-
部落格發表
- 添加發表部落格表單及校驗邏輯
- 新增部落格模型,用來儲存部落格
- 新增部落格校驗儲存
- 部落格展示(練習)