Flask基础学习3

时间:2024-03-01 14:20:30

参考视频: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 %}
  <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.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/<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')))

搜索功能的实现

@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