参考视频:41-【实战】答案列表的渲染_哔哩哔哩_bilibili
flask 实现发送短信功能
pip install flask-mail # 安装依赖
我这里用登录的网易邮箱获取的授权码(登录QQ邮箱的授权码总是断开收不到邮件),
# config
# config mail
MAIL_SERVER = 'smtp.163.com'
MAIL_USE_SSL = True
MAIL_PORT = 465
MAIL_USERNAME = 'xxx@163.com'
MAIL_PASSWORD='xxx'
MAIL_DEFAULT_SENDER = 'xxx@163.com'
@bp.route('/mail/test')
def mail_test():
message = Message(subject='mail test',recipients=['xxx@qq.com'],body='xxx')
mail.send(message)
运行结果:
邮箱发送验证功能实现
@bp.route('/captcha/email')
def get_mail_cptcha():
email = request.args.get('email')
source= string.digits*4
cap = random.sample(source,4)
cap = ''.join(cap)
message = Message(subject='菜鸟学习测试', recipients=[email], body='你的验证码是:{}'.format(cap))
mail.send(message)
email_capt = EmailCaptchModel(email=email,captcha=cap)
db.session.add(email_capt)
db.session.commit()
return jsonify({'code':200,'message':'','data':None})
查看DB数据
注册表单验证实现:
# blueprints/forms.py
import wtforms
from wtforms.validators import Email,Length,EqualTo
from models import UserModel,EmailCaptchModel
class RegisterForm(wtforms.Form):
email = wtforms.StringField(validators=[Email(message="邮箱格式错误!")])
captcha = wtforms.StringField(validators=[Length(min=4,max=4,message="验证码格式错误!")])
username = wtforms.StringField(validators=[Length(min=3,max=20,message="用户名格式错误!")])
password = wtforms.StringField(validators=[Length(min=3,max=20,message="密码长度为4-20位!")])
password_confirm = wtforms.StringField(validators=[EqualTo('password',message="两次输入的错误不一致!")])
def validate_email(self, field):
email = field.data
user = UserModel.query.filter_by(email=email).first()
if user:
raise wtforms.ValidationError(message='该邮箱已经被注册!')
def validate_captcha(self,field):
captcha = field.data
email = self.email.data
captcha_model = EmailCaptchModel.query.filter_by(email=email,captcha=captcha).first()
if not captcha_model:
print('邮箱或验证码格式错误!')
# raise wtforms.ValidationError(message='邮箱或验证码格式错误!')
# else:
# db.session.delete(captcha_model)
# db.session.commit()
注册功能后端的实现
# blueprints/auth.py
@bp.route('/register',methods = ['POST','GET'])
def register():
if request.method == 'GET':
return render_template('regist.html')
form = RegisterForm(request.form)
if form.validate():
email = form.email.data
username= form.username.data
password = form.password.data
user= UserModel(email=email,username=username,password=generate_password_hash(password))
db.session.add(user)
db.session.commit()
return redirect(url_for('auth.login'))
else:
print(form.data)
print(form.errors)
return redirect(url_for('auth.register'))
运行结果:
登录功能后端的实现,并将session信息加密保存到cookie中
# forms.py
class LoginForm(wtforms.Form):
email = wtforms.StringField(validators=[Email(message="邮箱格式错误!")])
print(wtforms.validators.Email)
password = wtforms.StringField(validators=[Length(min=4, max=20, message="密码长度为4-20位!")])
# auth.py
@bp.route('/login',methods = ['POST','GET'])
def login():
if request.method == 'GET':
return render_template('login.html')
form = LoginForm(request.form)
print(form.data)
if form.validate():
email = form.email.data
password = form.password.data
user = UserModel.query.filter_by(email=email).first()
if not user:
print('邮箱在数据库中不存在')
return redirect(url_for('auth.login'))
if check_password_hash(user.password,password):
# cookie 存在浏览器上
# flsk的session 是加密存在在cookie
session['user.id'] = user.id
return redirect(url_for('auth.index'))
else:
print('密码错误')
return redirect(url_for('auth.login'))
else:
print(form.errors)
return redirect(url_for('auth.login'))
注意: 配置session信息时要配置自定义密钥,否则会报错
# 配置session
SECRET_KEY = 'DSAFSDFASFASDFADFSDSASFD' # 无要求,自定义
两个钩子的运用及实现
python
# from flask import g
# 全局变量g
@login_required
def my_before_request():
user_id = session.get('user_id')
if user_id:
user = UserModel.query.get(user_id)
setattr(g,'user',user)
else:
setattr(g,'user',None)
@app.context_processor
def my_context_processor():
return {'user':g.user}
配置用户的登录名称显示及注销功能的实现
python
{% if user %}
<li><a href="#">{{ user.username }}</a></li>
<li><a href="{{ url_for('auth.logout') }}">注销</a></li>
{% else %}
<li><a href="{{url_for('auth.login')}}">登录</a></li>
<li><a href="{{url_for('auth.register')}}">注册</a></li>
{% endif %}
python
@bp.route('/logout')
def logout():
session.clear()
return render_template('index.html')
发布问答后端接口的实现
python
# model.py
class QuestionModel(db.Model):
__tablename__ = 'question'
id = db.Column(db.Integer,primary_key=True,autoincrement=True)
title = db.Column(db.String(1000),nullable=False)
content = db.Column(db.Text,nullable=False)
create_time = db.Column(db.DateTime,default=datetime.now())
author_id = db.Column(db.Integer,db.ForeignKey('user.id'))
author = db.relationship(UserModel,backref = 'questions')
# form 验证器
class QuestionForm(wtforms.Form):
title = wtforms.StringField(validators=[Length(min=4, max=100, message="标题格式错误!")])
context = wtforms.StringField(validators=[Length(min=3, max=200, message="内容格式错误")])
python
from flask import Blueprint,request,render_template
from .forms import QuestionForm
from decorators import login_required
from models import QuestionModel
from exts import db
bp=Blueprint('qa',__name__,url_prefix='/qa')
@bp.route('/question',methods = ['POST','GET'])
def question():
if request.method == 'GET':
return render_template('question.html')
else:
form =QuestionForm(request.form)
print(form.data)
if form.validate():
title = form.title.data
context = form.context.data
question = QuestionModel(title=title,content=context,author=g.user)
db.session.add(question)
db.session.commit()
return render_template('index.html')
else:
print(form.errors)
return render_template('question.html')
登录装饰器的实现,只有在登录后才可进行发布问答
python
# 自定义登录装饰器
from functools import wraps
from flask import g,redirect,url_for
def login_required(func):
# 保留func的信息
@wraps(func)
def inner(*args,**kwargs):
if g.user:
return func(*args,**kwargs)
else:
return redirect(url_for('auth.login'))
return inner
# 配置装饰器
@login_required
def question()
问答列表首页功能实现:
python
@bp.route('/')
def index():
# 根据创建时间倒序排列
questions = QuestionModel.query.order_by(QuestionModel.create_time.desc()).all()
return render_template('index.html',questions=questions)
发布问答详细的功能实现
python
@bp.route('/qa/detail/<qa_id>')
def qa_detail(qa_id):
question=QuestionModel.query.get(qa_id)
return render_template('detail.html',question=question)
@bp.post('/answer/public')
@login_required
def public_answer():
form = AnswerForm(request.form)
if form.validate():
content = form.context.data
question_id = form.question_id.data
answer = AnswerModel(content=content,question_id=question_id,author_id=g.user.id)
db.session.add(answer)
db.session.commit()
return redirect(url_for('qa.qa_detail',qa_id=question_id))
else:
print(form.errors)
return redirect(url_for('qa.qa_detail', qa_id=request.form.get('question_id')))
搜索功能的实现
python
@bp.route('/search')
def search():
q = request.args.get('q')
# 搜索标题的关键字
questions= QuestionModel.query.filter(QuestionModel.title.contains(q)).all()
return render_template('index.html',questions=questions)
okok