Flask-BBS论坛项目实战7-完成前台相关功能

注册功能后端逻辑

用户注册要把注册的表单提交上来,因此,我要先对表单进行验证,编辑front.forms

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
from apps.forms import BaseForm
from wtforms import StringField
from wtforms.validators import Regexp, Length, EqualTo, ValidationError
from utils import xcache
from .models import FrontUser


class SignUpForm(BaseForm):
telephone = StringField(validators=[Regexp(r'1[35678]\d{9}', message='手机号码格式错误')])
sms_captcha = StringField(validators=[Regexp(r'\w{6}', message='短信验证码错误')])
username = StringField(validators=[Length(2,20, message='用户名格式错误')])
password1 = StringField(validators=[Length(5, 30, message='密码格式错误')])
password2 = StringField(validators=[EqualTo('password1', message='两次密码不一致')])
graph_captcha = StringField(validators=[Regexp(r'\w{4}', message='图形验证码错误')])

def validate_telephone(self, field):
user = FrontUser.query.filter_by(telephone=field.data).first()
if user:
raise ValidationError('该手机号已被注册')

def validate_sms_captcha(self, field):
telephone = self.telephone.data
sms_captcha = field.data

sms_captcha_mem = xcache.get(telephone)
print('用户输入的验证码:{}'.format(sms_captcha))
print( '服务器存储的验证码:{}'.format(sms_captcha_mem))
if not sms_captcha_mem or sms_captcha_mem != sms_captcha:
raise ValidationError(message='短信验证码错误')

def validate_graph_captcha(self, field):
graph_captcha = field.data
#因为图形验证码存储的key和值都是一样的,所以我们只要判断key是否存在就行
if not xcache.get(graph_captcha.lower()):
raise ValidationError(message='图形验证码错误')

然后就是注册的视图处理POST请求了, 编辑front.views.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class SignUpViews(views.MethodView):
def get(self):
return render_template('front/front_signup.html')

def post(self):
signup_form = SignUpForm(request.form)
if signup_form.validate():
telephone = signup_form.telephone.data
username = signup_form.username.data
password = signup_form.password1.data
user = FrontUser(telephone=telephone, username=username, password=password)
db.session.add(user)
db.session.commit()
return xjson.json_success('恭喜您,注册成功')
else:
return xjson.json_param_error(signup_form.get_error())

注册功能前端逻辑

前端我们通过ajax来请求,编辑front_signup.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
...
//注册
$(function () {
$('#submit-btn').click(function (event) {
event.preventDefault();
var telephone_input = $("input[name='telephone']");
var sms_captcha_input = $("input[name='sms_captcha']");
var username_input = $("input[name='username']");
var password1_input = $("input[name='password1']");
var password2_input = $("input[name='password2']");
var graph_captcha_input = $("input[name='graph_captcha']")

var telephone = telephone_input.val();
var sms_captcha = sms_captcha_input.val();
var username = username_input.val();
var password1 = password1_input.val();
var password2 = password2_input.val();
var graph_captcha = graph_captcha_input.val();

bbsajax.post({
'url': '/signup/',
'data': {
'telephone': telephone,
'sms_captcha': sms_captcha,
'username': username,
'password1': password1,
'password2': password2,
'graph_captcha': graph_captcha
},
'success': function (data) {
if (data['code'] === 200){
//注册成功跳转到首页
window.location = '/';
}else{
xtalert.alertInfo(data['message']);
}
},
'fail': function (error) {
xtalert.alertNetworkError();
}

});
})
});

这样注册功能就OK,注册成功后会跳转到首页。

注册完成跳转回上一个页面

现在我们的需求是,用户直接进入注册页面注册,则注册成功后跳到首页,如果从本站的其他页面访问到注册页面注册,则注册成功后跳转到上一个页面。

那么我们可以在注册的视图函数中的get请求获取到referrer(上个页面,也就是从哪个页面跳过来的url)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#写一个检测referre的函数放到safeutils.py中,该文件放到utils下面
from utils import safeutils


class SignUpViews(views.MethodView):
def get(self):
#获取上一个页面的url
return_to = request.referrer
#referrer不一定会存在,比如直接访问的登录页面
#并且它不等于登录页面的url
#并且这个referre是个安全的url,防止恶意者去伪造它,被跳转到其它恶意的网站
if return_to and return_to !=request.url and safeutils.is_safe_url(return_to):
#把这个url传入到模板中
return render_template('front/front_signup.html', return_to=return_to)
return render_template('front/front_signup.html')

...

safeutils
1
2
3
4
5
6
7
8
from urllib.parse import urlparse,urljoin
from flask import request

def is_safe_url(target):
ref_url = urlparse(request.host_url)
test_url = urlparse(urljoin(request.host_url, target))
return test_url.scheme in ('http', 'https') and \
ref_url.netloc == test_url.netloc

url传入到frony_signup.html,我在这个页面找个位置接收它

1
<span style="display:none;" id="return-to-span">{{ return_to }}</span>

然后我们的js就可以获取到这个值,编辑front_signup.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

//注册
$(function(){
$("#submit-btn").click(function(event){
event.preventDefault();
var telephone_input = $("input[name='telephone']");
var sms_captcha_input = $("input[name='sms_captcha']");
var username_input = $("input[name='username']");
var password1_input = $("input[name='password1']");
var password2_input = $("input[name='password2']");
var graph_captcha_input = $("input[name='graph_captcha']");

var telephone = telephone_input.val();
var sms_captcha = sms_captcha_input.val();
var username = username_input.val();
var password1 = password1_input.val();
var password2 = password2_input.val();
var graph_captcha = graph_captcha_input.val();

bbsajax.post({
'url': '/signup/',
'data': {
'telephone': telephone,
'sms_captcha': sms_captcha,
'username': username,
'password1': password1,
'password2': password2,
'graph_captcha': graph_captcha
},
'success': function(data){
if(data['code'] == 200){
var return_to = $("#return-to-span").text();
if(return_to){
window.location = return_to;
}else{
window.location = '/';
}
}else{
xtalert.alertInfo(data['message']);
}
},
'fail': function(){
xtalert.alertNetworkError();
}
});
});
});

前台登录页面

我们的注册页面和登录页面有很多相似之处,因此,也可以基于一个模板来实现。
首先创建一个模板html,命名为front_signbase.html,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}{% endblock %}</title>
<meta name="csrf-token" content="{{ csrf_token() }}">
<script src="http://cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script>
<link href="http://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<script src="http://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="{{ url_for('static', filename='common/js/bbsparam.js') }}"></script>
<script src="{{ url_for('static', filename='front/js/front_signup.js') }}"></script>
<script src="{{ url_for('static', filename='common/js/bbsajax.js') }}"></script>
<script src="{{ url_for('static', filename='common/js/md5.js') }}"></script>
<link href="{{ url_for('static', filename='common/sweetalert/sweetalert.css') }}" rel="stylesheet">
<script src="{{ url_for('static', filename='common/sweetalert/sweetalert.min.js') }}"></script>
<script src="{{ url_for('static', filename='common/sweetalert/xtalert.js') }}"></script>

<style>
body{
background-color: #8CD4F5;
}

.page-title {
text-align: center;
padding-top: 50px;
}
.out-box{
width: 845px;
background-color: white;
margin: 0 auto;
margin-top: 50px;

}

.page-title {
text-align: center;
padding-top: 80px;
}

.form-box{
padding-top: 50px;
padding-bottom: 50px;
width: 300px;
margin: 0 auto;
}
.captcha-addon{
padding: 0; //这是内边距为0,因为input-group-addon有设置内边距
overflow: hidden; //当里面的元素超出则隐藏
}

#captcha-img{
height: 32px; //设置图片的高度为32px
cursor: pointer; //当鼠标移到图片上变成手的图标
}

</style>
{% block head %}{% endblock %}
</head>
<body>

<div class="out-box">
<h2 class="page-title">{% block h2_block %}{% endblock %}</h2>
<div class="form-box">
{% block form_content %}{% endblock %}
<span style="display:none;" id="return-to-span">{{ return_to }}</span>
</div>

</div>

</body>
</html>

然后修改注册页面front_signup.html,继承front_signbase.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

{% extends 'front/front_signbase.html' %}

{% block title %}注册-BBS论坛{% endblock %}

{% block h2_block %}注册BBS论坛{% endblock %}

{% block form_content %}
<div class="form-group">
<div class="input-group">
<input type="text" class="form-control" name="telephone" placeholder="手机号码">
<span class="input-group-btn">
<button id="sms-captcha-btn" class="btn btn-default">发送验证码</button>
</span>
</div>
</div>
<div class="form-group">
<input type="text" class="form-control" name="sms_captcha" placeholder="短信验证码">
</div>
<div class="form-group">
<input type="text" class="form-control" name="username" placeholder="用户名">
</div>
<div class="form-group">
<input type="password" class="form-control" name="password1" placeholder="密码">
</div>
<div class="form-group">
<input type="password" class="form-control" name="password2" placeholder="确认密码">
</div>
<div class="form-group">
<div class="input-group">
<input type="text" class="form-control" name="graph_captcha" placeholder="图形验证码">
<span class="input-group-addon captcha-addon">
<img id="captcha-img" src="{{ url_for('common.graph_captcha') }}">
</span>
</div>
</div>
<div class="form-group">
<button class="btn btn-warning btn-block" id="submit-btn">立即注册</button>
</div>
{% endblock %}

新建注册页面front_signin.html,继承front_signbase.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{% extends 'front/front_signbase.html' %}

{% block title %}登录-BBS论坛{% endblock %}

{% block h2_block %}登录BBS论坛{% endblock %}

{% block form_content %}
<div class="form-group">
<input type="text" class="form-control" name="telephone" placeholder="手机号码">
</div>
<div class="form-group">
<input type="password" class="form-control" name="password" placeholder="密码">
</div>
<div class="checkbox">
<label>
<input type="checkbox" name="remember" value="1">记住我
</label>
</div>
<div class="form-group">
<button class="btn btn-warning btn-block" id="submit-btn">立即登录</button>
</div>
<div class="form-group">
<a href="{{ url_for("front.signup") }}" class="signup-link">没有账号?立即注册</a>
<a href="#" class="resetpwd-link" style="float:right;">找回密码</a>
</div>
{% endblock %}

然后编写一个登录的视图,编辑front.view.py

1
2
3
4
5
6
7
8
9
10
11
12
class SignInViews(views.MethodView):
def get(self):
return_to = request.referrer
if return_to and return_to != request.url and safeutils.is_safe_url(return_to):
return render_template('front/front_signin.html', return_to=return_to)
return render_template('front/front_signin.html')

def post(self):
pass


bp.add_url_rule('/signin/', view_func=SignInViews.as_view('signin'))

输入http://127.0.0.1:5000/signin/ 就能查看效果

前台登录功能实现

后台逻辑

首先进行表单验证, 编辑front.froms.py

1
2
3
4
5
...
class SignInForm(BaseForm):
telephone = StringField(validators=[Regexp(r'1[35678]\d{9}', message='手机号码格式错误')])
password = StringField(validators=[Length(5, 30, message='密码格式错误')])
remember = StringField()

在config.py中配置一个用作保存前台session的常量

1
FRONT_USER_ID = 'WFQQ132FEVFW'

然后写视图了,编辑front.view.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
...
import config
from .forms import SignInForm

class SignInViews(views.MethodView):
def get(self):
return_to = request.referrer
if return_to and return_to != request.url and safeutils.is_safe_url(return_to):
return render_template('front/front_signin.html', return_to=return_to)
return render_template('front/front_signin.html')

def post(self):
signin_form = SignInForm(request.form)
if signin_form.validate():
telephone = signin_form.telephone.data
password = signin_form.password.data
remember = signin_form.remember.data
user = FrontUser.query.filter_by(telephone=telephone).first()
if user and user.check_password(password):
session[config.FRONT_USER_ID] = user.id
if remember:
session.permanent = True
return xjson.json_success('登录成功')
else:
return xjson.json_param_error('手机号或密码错误')
else:
return xjson.json_param_error(signin_form.get_error())


bp.add_url_rule('/signin/', view_func=SignInViews.as_view('signin'))

前端逻辑

在static/front/js下新建个js文件front_signin.js(获取注册按钮,配置它的点击事件)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
$(function(){
$("#submit-btn").click(function (event) {
event.preventDefault();
var telephone_input = $("input[name='telephone']");
var password_input = $("input[name='password']");
var remember_input = $("input[name='remember']");

var telephone = telephone_input.val();
var password = password_input.val();
var remember = remember_input.checked ? 1 : 0; //如果有选择返回1否则返回0


bbsajax.post({
'url': '/signin/',
'data': {
'telephone': telephone,
'password': password,
'remember': remember
},
'success': function (data) {
if(data['code'] == 200){
var return_to = $("#return-to-span").text();
if(return_to){
window.location = return_to;
}else{
window.location = '/';
}
}else{
xtalert.alertInfo(data['message']);
}
}
});
});
});

把front_signin.js引入到front_signin.html中

1
2
3
{% block head %}
<script src="{{ url_for('static', filename='front/js/front_signin.js') }}"></script>
{% endblock %}

把front_signbase中的front_signup.js引入去掉,在front_signup.html中引入它

1
2
3
{% block head %}
<script src="{{ url_for('static', filename='front/js/front_signup.js') }}"></script>
{% endblock %}

-------------本文结束感谢您的阅读-------------