flask 检测post是否为空_使用Flask搭建一个校园论坛-4
在上一節中完成了注冊功能的前期準備工作,在這一節內容中將完成用戶注冊、登錄功能。
1.知識預覽
在本屆中將學習到以下內容的知識
2.用戶注冊
在前端中form表單是用的比較多的東西,我們可以使用wtforms這個框架,直接通過后端代碼來渲染前端表單。新建bbs/forms.py文件,嵌入以下代碼
from flask_wtf import FlaskForm from wtforms import StringField, SubmitField, SelectField, BooleanField, TextAreaField, FileField, Label, HiddenField, PasswordFieldclass BaseUserForm(FlaskForm):user_name = StringField(u'用戶名',validators=[DataRequired(message='用戶名不能為空'),Length(min=1, max=16, message='用戶名長度限定在1-16位之間'),Regexp('^[a-zA-Z0-9_]*$',message='用戶名只能包含數字、字母以及下劃線.')],render_kw={'placeholder': '請輸入用戶名長度1-16之間'})nickname = StringField(u'昵稱',validators=[DataRequired(message='昵稱不能為空'),Length(min=1, max=20, message='昵稱長度限定在1-20位之間')],render_kw={'placeholder': '請輸入昵稱長度1-20之間'})user_email = StringField(u'注冊郵箱',render_kw={'placeholder': '請輸入注冊郵箱', 'type': 'email'})submit = SubmitField(u'注冊', render_kw={'class': 'btn btn-success btn-xs'})在上面的代碼中,首先導入相關的庫,然后新建了一個BaseUserForm的類,因為用戶的信息在很多表單中使用到了,因此可以將共同的屬性剝離出來,然后在不同的場合繼承該基類,并且可以根據不同的場合在子類中定制我們的表單屬性,這樣就可以降低代碼的冗余量。如果在每個需要使用到用戶信息的表單代碼中寫入同樣的內容,那么久顯得代碼很臃腫了。
BaseUserForm類中使用到了wtforms中的一些屬性,比如StringField就相當于是我們前端的input標簽,SubmitField就相當于是<input type="submit">,具體可以去看wtforms的官方文檔。
繼續在上面的文件中嵌入如下代碼,新建注冊表單類
class RegisterForm(FlaskForm):user_name = StringField(u'用戶名',validators=[DataRequired(message='用戶名不能為空'),Length(min=1, max=16, message='用戶名長度限定在1-16位之間'),Regexp('^[a-zA-Z0-9_]*$',message='用戶名只能包含數字、字母以及下劃線.')],render_kw={'placeholder': '請輸入用戶名長度1-16之間'})nickname = StringField(u'昵稱',validators=[DataRequired(message='昵稱不能為空'),Length(min=1, max=20, message='昵稱長度限定在1-16位之間')],render_kw={'placeholder': '請輸入昵稱長度1-20之間'})user_email = StringField(u'注冊郵箱',validators=[DataRequired(message='注冊郵箱不能為空'),Length(min=4, message='注冊郵箱長度必須大于4')],render_kw={'placeholder': '請輸入注冊郵箱', 'type': 'email'})password = StringField(u'密碼',validators=[DataRequired(message='用戶密碼不能為空'),Length(min=8, max=40, message='用戶密碼長度限定在8-40位之間'),EqualTo('confirm_pwd', message='兩次密碼不一致')],render_kw={'placeholder': '請輸入密碼', 'type': 'password'})confirm_pwd = StringField(u'確認密碼',validators=[DataRequired(message='用戶密碼不能為空'),Length(min=8, max=40, message='用戶密碼長度限定在8-40位之間')],render_kw={'placeholder': '輸入確認密碼', 'type': 'password'})colleges = SelectField(u'學院', choices=[(1, '計算機')])submit = SubmitField(u'注冊', render_kw={'class': 'source-button btn btn-primary btn-xs mt-2'})def __init__(self, *args, **kwargs):super(RegisterForm, self).__init__(*args, **kwargs)cols = College.query.all()self.colleges.choices = [(col.id, col.name) for col in cols]def validate_user_name(self, filed):if User.query.filter_by(username=filed.data).first():raise ValidationError('用戶名已被注冊.')def validate_user_email(self, filed):if User.query.filter_by(email=filed.data.lower()).first():raise ValidationError('郵箱已被注冊.')def validate_nickname(self, filed):if User.query.filter_by(nickname=filed.data).first():raise ValidationError('昵稱已被注冊')因為學院的選項有許多,我們可以在類的構造函數中通過數據庫去獲取數據庫中已經存在的學院,然后將其設置到colleges類屬性的choices值上,這樣當我們打開頁面渲染表單時,數據就會自動渲染到select標簽option上去了,如下圖
然后還新建了三個函數validate_user_name、validate_user_email以及validate_nickname,這三個函數主要是用來判斷email、username、nickname三個字段的唯一性,因為在數據庫建表的時候將這三個字段設置為unique=True,因此在這里需要做一個唯一性的判斷。
使用wtforms時,我們可以通過validate_加上你需要校驗的屬性字段名稱來檢驗前端用戶輸入的數據是否符合標準。表單類的編寫已經完成,接下來就是整個注冊邏輯的實現了。新建bbs/templates/frontend/register.html文件,嵌入以下代碼
{% extends "frontend/base.html" %} {% import 'bootstrap/wtf.html' as wtf %} {% block title %}用戶注冊 {% endblock %} {% block content %}<body><main><div class="container"><div class="jumbotron pt-5 pb-1 mt-2"><div class="row"><div class="col-md-8"><h3 class="text-muted"><b>歡迎注冊加入狗子學院~</b></h3><hr class="bg-primary"><p><b>在這里你可以:</b></p><ul><li>瀏覽當下校園的一些趣事、雜談以及誰和誰的八卦</li><li>發布一些咸魚交易、尋物啟事等等</li><li>發現臭味相投的朋友、開拓自己的圈子</li></ul><img src="{{ url_for('static', filename='img/index.jpg') }}" class="rounded img-fluid"></div><div class="col-md-4"><div class="card mb-3 w-100 bg-light"><div class="card-header"><h4 class="text-muted"><strong>用戶注冊</strong></h4></div><div class="card-body">{% include "_flash.html" %}<form class="bs-component" action="/auth/register/" method="post">{{ form.csrf_token }}{{ wtf.form_field(form.user_name) }}{{ wtf.form_field(form.nickname) }}{{ wtf.form_field(form.user_email) }}{{ wtf.form_field(form.password) }}{{ wtf.form_field(form.confirm_pwd) }}{{ wtf.form_field(form.colleges) }}<label for="captcha">驗證碼</label><div class="input-group"><input type="text" class="form-control" name="captcha" id="captcha" placeholder="請輸入驗證碼" aria-required="true" aria-describedby="captcha" required><div class="input-group-append"><button class="btn btn-success" onclick="sendCapt()" id="sendCaptcha">發送</button></div></div><p class="p-hint">驗證碼發送成功,10分鐘內有效!</p>{{ form.submit }}<hr><small>已有賬號? <a style="text-decoration: none;" href="{{ url_for('.login') }}">登錄.</a></small></form></div></div></div></div></div></div></main>然后打開bbs/blueprint/frontend/auth.py文件,接著在上一節下面嵌入如下代碼
@auth_bp.route('/register/', methods=['GET', 'POST']) def register():colleges = College.query.all()form = RegisterForm()return render_template('frontend/register.html', colleges=colleges, form=form)在后端代碼中我們通過render_template函數返回了前端注冊頁面,并且攜帶注冊表單的實例參數。在前端html文件中,我們可以通過form.參數名的方式來進行表單渲染。同時還在前端文件中導入了bootstrap/wtf.html,這樣就可以將表單的樣式渲染成bootstrap的樣式,當然也可以不是bootstrap/wtf.html來渲染,在后端表單類中可以通過render_kw參數來指定我們表單的一些特定參數。
在前端頁面中我們還手動加入了一行驗證碼輸入框,點擊發送按鈕就可以將驗證碼發送到用戶填寫的郵箱當中去了。為什么不將此輸入框寫到后端表單中去?因為那樣不好處理前端樣式了。
訪問http://127.0.0.1/auth/register/ 將會看到如下頁面:
在注冊頁面中是需要用戶填寫郵箱收到的驗證碼,因此我們需要在后端代碼中實現發送郵件的功能。發送郵件的功能是通過flask-email來實現的,打開bbs/extensions.py文件,加入下面的代碼,然后在__init__.py文件中進行注冊。
from flask_mail import Mail mail = Mail()在使用發送郵件功能之前,首先我們需要到qq郵箱或者網易郵箱或者其他可以使用的郵箱申請SMTP服務,具體流程可以某度某歌搜索一下,這里就不再累述。將申請到的私密信填入到.env文件中
MAIL_SERVER='smtp.qq.com' MAIL_USERNAME='你的qq郵箱名' MAIL_PASSWORD='qq郵箱秘鑰不是登錄密碼是申請SMTP那串無規則秘鑰'然后在bbs/setting.py文件中加入下面的代碼
class BaseConfig(object): # 省略之前代碼BBS_MAIL_SUBJECT_PRE = '[狗子學院]'MAIL_SERVER = os.getenv('MAIL_SERVER')MAIL_PORT = 465MAIL_USE_SSL = TrueMAIL_USERNAME = os.getenv('MAIL_USERNAME')MAIL_PASSWORD = os.getenv('MAIL_PASSWORD')MAIL_DEFAULT_SENDER = ('BBS Admin', MAIL_USERNAME)新建bbs/email.py文件,并將下面代碼寫入其中。
from threading import Threadfrom bbs.extensions import mail from flask_mail import Message from flask import current_app, render_templatedef async_send_mail(app, msg):with app.app_context():mail.send(msg)def send_email(to_mail, subject, template, **kwargs):message = Message(current_app.config['BBS_MAIL_SUBJECT_PRE'] + subject,recipients=[to_mail],sender=current_app.config['MAIL_USERNAME'])message.body = render_template(template + '.txt', **kwargs)message.html = render_template(template + '.html', **kwargs)th = Thread(target=async_send_mail, args=(current_app._get_current_object(), message))th.start()return th在send_email函數中,使用了render_template()來渲染了郵件消息body以及html參數,因此需要先將這兩個模板準備好。新建bbs/templates/email/verifyCode.html 與bbs/templates/email/verifyCode.txt 文件,將下面的代碼寫到文件中去
verifyCode.html
<h3 style="font-weight: bold;font-size: 18px">Hello {{ username }},</h3> <p>Welcome to join the <a href="http://bbs.2dogz.cn">狗子學院</a>!This is your register captcha below here.</p> <h1><strong>{{ ver_code }}</strong></h1> <h5><b><i>The captcha will expire after 10 minutes.</i></b></h5> <p style="color: red; font-style: italic"> If this operate is not by yourself, please change your password right now!Maybe your account was cracked.</p> <small>(Please do not reply to this notification, this inbox is not monitored.)</small>verifyCode.txt
Hello {{ username }} Welcome to Blogin! Welcome to join the 狗子學院!This is your register captcha below here. {{ ver_code }} The captcha will expire after 10 minutes. If this operate is not by yourself, please change your password right now!Maybe your account was cracked.</p>(Please do not reply to this notification, this inbox is not monitored.)然后開始編寫發送郵件的后端邏輯代碼,新建bbs/blueprint/frontend/normal.py 文件,因為發送郵件屬于通用行為,因此將其放入normal.py模塊中,將以下代碼嵌入其中
from flask import Blueprint, send_from_directory, request, jsonify from bbs.extensions import db from bbs.email import send_email from bbs.models import VerifyCode, Gender, Role, College@normal_bp.route('/send-email/', methods=['POST']) def send():to_email = request.form.get('user_email')username = request.form.get('user_name')ver_code = generate_ver_code()send_email(to_mail=to_email, subject='Captcha', template='email/verifyCode', username=username,ver_code=ver_code)# 判斷是否已經存在一個最新的可用的驗證碼,以確保生效的驗證碼是用戶收到最新郵件中的驗證碼exist_code = VerifyCode.query.filter(VerifyCode.who == to_email, VerifyCode.is_work == 1).order_by(VerifyCode.timestamps.desc()).first()if exist_code:exist_code.is_work = Falsent = datetime.datetime.now()et = nt + datetime.timedelta(minutes=10)verify_code = VerifyCode(val=ver_code, who=to_email, expire_time=et)db.session.add(verify_code)db.session.commit()return jsonify({'tag': 1, 'info': '郵件發送成功!'})我們根據請求中的keyword來獲取前端發送過來的請求參數,然后調用send_email()函數進行發送郵件,同時將生成的隨機驗證碼放入到郵件消息體中去。
然后判斷數據庫中是否已經存在了屬于該注冊用戶的驗證碼,如果有則將它設置為過期的,然后將新的驗證碼存入到數據庫中,并設置過期時間為10分鐘。這里是通過MySQL來保存的驗證碼信息,也有其他方法來保存驗證碼信息,比如使用redis來保存,redis可以設置字段過期時間,如果達到了這個時間,再去取這個字段的值就會為None。
接著我們來處理前端發送郵件的代碼,打開bbs/templates/frontend/register.html文件,加入下面的代碼
<main> ... </main> <script>let time = 60;let reg = /^w+((-w+)|(.w+))*@[A-Za-z0-9]+((.|-)[A-Za-z0-9]+)*.[A-Za-z0-9]+$/;function sendCapt(){let $sendBtn = $("#sendCaptcha");let $email = $("#user_email");let $username = $("#user_name");if ($username.val() === '' || $email.val() === '' || !reg.test($email.val())){return false;}$sendBtn.attr('disabled', true);getRandomCode($sendBtn);$.ajax({url: '/normal/send-email/',type: 'post',data: {'user_name': $username.val(), 'user_email': $email.val()},success: function (res){if (res.tag){$(".p-hint").slideDown(500).delay(3000).hide(500);}}})}//倒計時function getRandomCode(obj) {if (time === 0) {time = 60;obj.text('發送');obj.attr('disabled', false);return;} else {time--;obj.text(time+'(秒)');}setTimeout(function() {getRandomCode(obj);},1000);}</script>通過ajax向后端/normal/send-email/發送請求,同時將email與username傳遞到后端,同時將發送驗證碼按鈕置為不可點擊狀態,間隔60s才能發送一次,并在前端頁面給用戶一個提示信息。這樣發送驗證碼郵件的整個流程就完成了,接下里要處理用戶點擊注冊按鈕之后的邏輯。
我們使用的是wtfforms來渲染的后端表單,并且在某些表單字段中加入了一些限制信息。在后端代碼中,我們可以通過wtfforms的示例來驗證我們的表單,打開bbs/blueprint/frontend/auth.py 加入下面的代碼
@auth_bp.route('/register/', methods=['GET', 'POST']) def register(): ...if form.validate_on_submit():username = form.user_name.datanickname = form.nickname.datapassword = form.confirm_pwd.dataemail = form.user_email.datacollege = form.colleges.datacaptcha = request.form.get('captcha')code = VerifyCode.query.filter(VerifyCode.who == email, VerifyCode.is_work == 1).order_by(VerifyCode.timestamps.desc()).first()if code:if code.val != int(captcha):flash('驗證碼錯誤!', 'danger')return redirect(request.referrer)elif code.expire_time < datetime.datetime.now():flash('驗證碼已過期!', 'danger')return redirect(request.referrer)else:flash('請先發送驗證碼到郵箱!', 'info')return redirect(request.referrer)user = User(username=username,college_id=college,nickname=nickname,email=email,password=password,status_id=1)user.generate_avatar()user.set_password(password)code.is_work = Falsedb.session.add(user)db.session.commit()flash('注冊成功,歡迎加入二狗學院!', 'success')return redirect(url_for('.login'))通過form.validate_on_submit()來判斷提交的表單是否通過了驗證,通過驗證之后通過form.字段名.data來獲取對應表單字段的值,然后根據郵箱來查找上一步數據庫保存的驗證碼,如果不存在驗證碼,則提示用戶發送驗證碼到郵箱,因為存在著一種可能用戶亂填一個驗證碼,而不發送驗證碼到郵箱。然后判斷驗證碼是否正確或者過期,如果未通過,則發送對應的提示消息提示用戶,如果通過,則將用戶信息保存到數據庫中,然后重定向到登錄頁面。至此,用戶注冊的功能就已經完成了。
用戶登錄
相比注冊功能,用戶登錄就比較簡單了。新建bbs/templates/frontend/login.html文件,該文件為用戶登錄的前端頁面模板文件,同樣的我們使用wtfforms來渲染表單,在文件中嵌入下面的代碼
{% extends "frontend/base.html" %} {% import 'bootstrap/wtf.html' as wtf %} {% block title %}用戶登錄 {% endblock %} {% block content %}<body><main><div class="container">{% include "_flash.html" %}<div class="jumbotron pt-5 pb-3 mt-5"><div class="row"><div class="col-md-8"><img src="{{ url_for('static', filename='img/index.jpg') }}" class="rounded img-fluid"></div><div class="col-md-4"><div class="card mb-3 w-100 bg-light align-self-center"><div class="card-header"><h4 class="text-muted"><strong>用戶登錄</strong></h4></div><div class="card-body "><form class="bs-component" action="/auth/login/" method="post">{{ form.csrf_token }}{{ wtf.form_field(form.usr_email) }}{{ wtf.form_field(form.password) }}{{ wtf.form_field(form.remember_me) }}{{ form.submit }}<hr><small>沒有賬號? <a style="text-decoration: none;" href="{{ url_for('.register') }}">注冊.</a></small></form></div></div></div></div></div></div></main></body> {% endblock %}代碼中的form.csrf_token 是一種防止csrf攻擊的手段,關于csrf攻擊具體可以百度,之后的代碼就跟注冊模板一樣,通過wtfforms進行表單渲染,因此我們需要新建一個渲染登錄表單的類,打開bbs/forms.py模塊,加入新建登錄表單的代碼,如下所示
class LoginForm(FlaskForm):usr_email = StringField(u'郵箱/用戶名', validators=[DataRequired(message='用戶名或郵箱不能為空')],render_kw={'placeholder': '請輸入郵箱或用戶名'})password = StringField(u'登錄密碼',validators=[DataRequired(message='登錄密碼不能為空'),Length(min=8, max=40, message='登錄密碼必須在8-40位之間')],render_kw={'type': 'password', 'placeholder': '請輸入用戶密碼'})remember_me = BooleanField(u'記住我')submit = SubmitField(u'登錄', render_kw={'class': 'source-button btn btn-primary btn-xs'})接著需要處理后端的登錄視圖函數,打開bbs/blueprint/frontend/auth.py模塊,新建一個視圖函數,代碼如下所示
from flask_login import current_user, login_user, logout_user @auth_bp.route('/login/', methods=['GET', 'POST']) def login():if current_user.is_authenticated:return redirect(url_for('index_bp.index'))form = LoginForm()if form.validate_on_submit():usr = form.usr_email.datapwd = form.password.datauser = User.query.filter(or_(User.username == usr, User.email == usr.lower())).first()if user is not None and user.status.name == '禁用':flash('您的賬號處于封禁狀態,禁止登陸!聯系管理員解除封禁!', 'danger')return redirect(url_for('.login'))if user is not None and user.check_password(pwd):if login_user(user, form.remember_me.data):flash('登錄成功!', 'success')return redirect(url_for('index_bp.index'))elif user is None:flash('無效的郵箱或用戶名.', 'danger')else:flash('無效的密碼', 'danger')return render_template('frontend/login.html', form=form)關于登錄邏輯處理,flask有一個十分流行好用的第三方庫flask-login,使用該第三庫我們可以很方便的處理登錄、退出權限控制等操作。
首先通過current_user來判斷用戶是否已經登錄了,如果登錄則返回主頁面。接著通過LoginForm示例來獲取前端登錄頁面傳遞過來的值,從數據庫獲取用戶的相關信息,首先判斷賬號是否被禁用了,如果被禁用則彈出提示信息,并返回給前端頁面。接著判斷用戶密碼是否匹配,如果不匹配則返回登錄頁面,并提示用戶密碼不匹配,反之則重定向到主頁。
這里的登錄成功重定向其實可以做的更加人性化,當用戶進入到需要登錄才能操作的頁面時候,這時候會自動跳轉到登錄頁面。如果用戶登錄成功,應該是返回前一個頁面而不是固定返回主頁。flask_login的login_required裝飾器重定向到登錄頁面的時候會帶一個next參數,因此我們可以通過此參數來讓用戶登錄成功之后重定向到上一頁,具體實現很簡單,就請讀者自主開發吧!這時候我們打開登錄頁面,可以看到如下頁面
到此,用戶登錄注冊功能就已經全部實現了,下一節將開始講述論壇主頁的實現。
教程中的資源文件可以進入我的github倉庫下載源代碼使用
倉庫連接?github.com論壇已經基本完成了,我已經部署到我的個人服務器上去了
主頁 -二狗學院?bbs.2dogz.cn感興趣的同學可以關注我的博客網站哦,會不定期更新一些程序員相關的博客哦!
Home - Blogin?2dogz.cn總結
以上是生活随笔為你收集整理的flask 检测post是否为空_使用Flask搭建一个校园论坛-4的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 7 win 卸载node_node怎么卸
- 下一篇: 桂圆多少钱一斤啊?