• Flask—08-建立自己的博客(02)


    博客项目

    上一篇内容完善

    • 自定义字段验证函数

      class RegisterForm(FlaskForm):
          ...
          def validate_username(self, field):
              user = User.query.filter(User.username == field.data).first()
              if user:
                  raise ValidationError('该用户名已注册,请选用其他名称注册')
      
          def validate_email(self, field):
              user = User.query.filter(User.email == field.data).first()
              if user:
                  raise ValidationError('该邮箱已注册,请选用其他邮箱注册')
      
    • 加密存储密码

      from werkzeug.security import generate_password_hash, check_password_hash
      
      class User(db.Model):
          ...
            @property
          def password(self):
              raise AttributeError('你瞅啥?!密码不可读')
      
          @password.setter
          def password(self, password):
              # 加密保存密码
              self.password_hash = generate_password_hash(password)
      
          # 密码校验,True:校验成功,False:校验失败
          def verify_password(self, password):
              return check_password_hash(self.password_hash, password)
      

    用户登录退出

    • 登录校验逻辑

      @user.route('/login/', methods=['GET', 'POST'])
      def login():
          form = LoginForm()
          if form.validate_on_submit():
              u = User.query.filter(User.username == form.username.data).first()
              if not u:
                  flash('无效的用户名')
              elif not u.confirmed:
                  flash('账户尚未激活,请激活后再登录')
              elif not u.verify_password(form.password.data):
                  flash('无效的密码')
              else:
                  flash('登录成功')
                  return redirect(url_for('main.index'))
          return render_template('user/login.html', form=form)
      
    • flask-login扩展

      • 说明:它是一个专门用来管理用户登录退出的扩展库,使用非常方便。
      • 安装:pip install flask-login
      • 使用:
      # 第一步:添加扩展
      from flask_login import LoginManager
      
      login_manager = LoginManager()
      
      def init_extensions(app):
          ...
          login_manager.init_app(app)
          # 指定登录端点
          login_manager.login_view = 'user.login'
          # 设置提示信息
          login_manager.login_message = '登录后才可访问'
      
      # 第二步:让模型类类基础子UserMixin类,以便拥有状态相关方法
      from flask_login import UserMixin
      
      class User(UserMixin, db.Model):
            ...
      
      # 第三步:实现回调函数(根据用户id返回用户对象)
      @login_manager.user_loader
      def load_user(uid):
          return User.query.get(uid)
      
      • 总结
      状态切换:
          login_user            # 还可以完成记住我的功能(时间也可以指定)
          logout_user
      状态查询:
          is_authenticated    # 登录状态
          is_anonymous        # 匿名状态
      路由保护:
          login_required        # 保护需要登录才可访问的路由
      当前用户:
          current_user        # 在模板中使用不需要分配
      

    用户信息管理

    • 详情信息展示
      • 添加点击跳转链接及逻辑
      • 书写详情页面的展示效果
    • 修改密码
      • 添加点击跳转链接
      • 模板文件中添加表单(原始密码、新密码、确认密码)
      • 添加点击提交校验的逻辑
    #修改密码
    @user.route('/modifypassword/',methods=['GET','POST'])
    def modify_password():
        form = ModifyPasswordForm()
        if form.validate_on_submit():
            if current_user.verify_password(form.password.data):
                current_user.password = form.password_new1.data
                flash('修改密码成功')
                return redirect(url_for('user.login'))
            else:
                flash('原始密码错误')
        return render_template('user/modify_password.html', form=form)
    
    • 修改邮箱
      • 添加点击跳转链接
      • 模板文件中添加表单(新邮箱)
      • 添加点击提交校验的逻辑(向新的邮箱地址发送确认邮件,需要携带用户信息及新邮箱地址)
      • 添加邮箱修改确认的校验视图函数(解析携带数据,修改用户邮箱)
    #修改邮箱
    @user.route('/modifyemail/',methods=['GET','POST'])
    def modify_email():
        form = ModifyEmailForm()
        if form.validate_on_submit():
            if current_user.email == form.email.data:
                s = Serializer(current_app.config['SECRET_KEY'],expires_in=3600)
                token = s.dumps({'id':current_user.id,'email':form.email_new.data})
                send_mail('邮箱修改',form.email_new.data,'email/modify_email_activate.html',username=current_user.username,token=token)
                flash('请前往新邮箱确认修改')
                return redirect(url_for('user.profile'))
            else:
                flash('原始邮箱不正确')
        return render_template('user/modify_email.html',form=form)
    
    • 找回密码
      • 在登录页面添加找回密码的跳转链接
      • 跳转的模板文件中添加表单(用户名/邮箱地址),提交改为下一步
      • 添加提交的校验逻辑(向用户的邮箱发送邮件,需要携带用户身份信息)
      • 添加重新设置密码的视图函数(给出再次设置密码的表单,并处理提交)
    # 找回密码
    @user.route('/findpassword/<way>/', methods=['GET', 'POST'])
    def find_password(way):
        form = FindPasswordForm()
        s = Serializer(current_app.config['SECRET_KEY'], expires_in=3600)
        if form.validate_on_submit():
            u = User.query.filter(
                or_(User.username == form.username_email.data, User.email == form.username_email.data)).first()
            if u:
                token = s.dumps({'id': u.id})
                # send_mail('通过用户名找回密码',u.email,'email/find_password_activate.html',username=u.username,token=token)
                flash('请前往邮箱确认找回密码')
            else:
                flash('用户名错误')
                return redirect(url_for('user.login'))
        return render_template('user/find_password.html', form=form)
    
    @user.route('/findpasswordactivate/<token>/', methods=['GET', 'POST'])
    def find_password_activate(token):
        form = NewPasswordForm()
        s = Serializer(current_app.config['SECRET_KEY'])
        try:
            data = s.loads(token)
        except SignatureExpired as e:
            flash('token已过期,找回密码失败')
            return redirect(url_for('user.login'))
        except BadSignature as e:
            flash('token有误,找回密码失败')
            return redirect(url_for('user.login'))
        u = User.query.get(data['id'])
        if form.validate_on_submit():
            u.password = form.password2.data
            db.session.add(u)
            flash('找回密码成功')
            return redirect(url_for('user.login'))
        return render_template('user/new_password.html', form=form)
    
    • 修改头像
      • 添加点击跳转的链接
      • 添加flask-uploads扩展
      • 添加上传文件表单及校验逻辑
      • 添加上传文件的处理(生成随机文件名、生成缩略图、展示上传文件)
      • 将头像文件名保存到数据库(User模型需要添加字段)
      • 练习:用户详情页面展示用户头像
    # 修改头像
    @user.route('/icon/', methods=['GET', 'POST'])
    def icon():
        form = UploadForm()
        if form.validate_on_submit():
            # 提取上传文件信息
            photo = form.photo.data
            # 提取文件后缀带点
            suffix = os.path.splitext(photo.filename)[1]
            # 生成随机文件名
            filename = random_string() + suffix
            # 用上传对象保存图片,并指定名字
            photos.save(photo, name=filename)
            # 拼接完整的图片路径
            pathname = os.path.join(current_app.config['UPLOADED_PHOTOS_DEST'], filename)
            # 缩略图操作,打开文件
            img = Image.open(pathname)
            # 设置图片尺寸
            img.thumbnail((32, 32))
            # 重新保存,指定完整路径
            img.save(pathname)
            # 如果当前用户用的是默认头像,则不用删除
            # 若果用的不是默认头像,为减少存储需要先删除原来的头像
            if current_user.icon != 'default.jpg':
                # os.remove删除项目本地文件
                os.remove(os.path.join(current_app.config['UPLOADED_PHOTOS_DEST'], current_user.icon))
            # 新的头像准备好了,添加到model里
            current_user.icon = filename
            # 手动保存,可有可无
            db.session.add(current_user)
        # 头像已经保存到了static和model中,为了展示头像,可以把图片的路径传给模板
        # 静态文件是固定格式   注:不能直接写图片名,那样就写死了
        # 不同用户有独自的头像,所以用current_user.icon进行拼接
        img_url = url_for('static', filename='upload/' + current_user.icon)
        return render_template('user/icon.html', form=form, img_url=img_url)
    

    博客管理

    • 博客发表
      • 添加发表博客表单及校验逻辑
      • 添加博客模型,用来保存博客
      • 添加博客校验保存
    • 博客展示(练习)
  • 相关阅读:
    Nginx
    Web 系统架构一般组成
    分布式系统常见的问题
    Scala + Thrift+ Zookeeper+Flume+Kafka配置笔记
    Spring Boot—21Actuator--监控
    Zookeeper
    Spring Boot—20Zookeeper
    Spring Boot—19Session
    Spring Boot—19Cache
    Spring Boot—18Redis
  • 原文地址:https://www.cnblogs.com/swjblog/p/9741794.html
Copyright © 2020-2023  润新知