参考视频: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*4cap = 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,EmailCaptchModelclass 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.datauser = UserModel.query.filter_by(email=email).first()if user:raise wtforms.ValidationError(message='该邮箱已经被注册!')def validate_captcha(self,field):captcha = field.dataemail = self.email.datacaptcha_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.datausername= form.username.datapassword = form.password.datauser= 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.datapassword = form.password.datauser = 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 是加密存在在cookiesession['user.id'] = user.idreturn 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' # 无要求,自定义
两个钩子的运用及实现
# 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}
配置用户的登录名称显示及注销功能的实现
{% 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 %}
@bp.route('/logout')
def logout():session.clear()return render_template('index.html')
发布问答后端接口的实现
# 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="内容格式错误")])
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.datacontext = form.context.dataquestion = 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')
登录装饰器的实现,只有在登录后才可进行发布问答
# 自定义登录装饰器
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()
问答列表首页功能实现:
@bp.route('/')
def index():# 根据创建时间倒序排列questions = QuestionModel.query.order_by(QuestionModel.create_time.desc()).all()return render_template('index.html',questions=questions)
发布问答详细的功能实现
@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.dataquestion_id = form.question_id.dataanswer = 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')))
搜索功能的实现
@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