参考视频: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' # 无要求,自定义
两个钩子的运用及实现
- # 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 %}
- {% else %}
- {% 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.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')
登录装饰器的实现,只有在登录后才可进行发布问答
- # 自定义登录装饰器
- 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/
' ) - 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')))
搜索功能的实现
- @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