Flask学习笔记_异步论坛(四)

news2024/9/23 11:26:29

Flask学习笔记_异步论坛(四)

  • 1.配置和数据库链接
    • 1.exts.py里面实例化sqlalchemy数据库
    • 2.config.py配置app和数据库信息
    • 3.app.py导入exts和config并初始化到app上
  • 2.创建用户模型并映射到数据库
    • 1.models/auth.py创建用户模型
    • 2.app.py导入模型并用flask-migrate管理数据库
    • 3.命令行migrate三部曲将模型映射到数据库
  • 3.登录与注册页面的get请求
    • 1.首先写登录和注册的前端页面
    • 2.写它们的view蓝图并导入到__init__中
    • 3.蓝图注册到app
  • 4.邮箱验证功能
    • 1.邮箱验证
    • 2.使用celery异步发送邮箱验证网络请求
    • 3.使用flask-caching缓存验证码并验证
    • 4.重构restful API
  • 5.注册页面的post请求
    • 5.1注册页面邮箱验证码的ajax请求
    • 5.2注册页面的图形验证码功能
    • 5.3注册页面的post提交
  • 6.登录页面的post请求
  • 7.首页

1.配置和数据库链接

1.exts.py里面实例化sqlalchemy数据库

from flask_sqlalchemy import SQLAlchemy
db=SQLAlchemy()

2.config.py配置app和数据库信息

#1.app配置
DEBUG=True
#2.数据库配置
DB_USERNAME="root"
DB_PASSWORD="1xxxx"
DB_HOST="127.0.0.1"
DB_PORT="3306"
DB_NAME="aforum"
DB_URI="mysql+pymysql://%s:%s@%s:%s/%s?charset=utf8mb4" % (DB_USERNAME,DB_PASSWORD,DB_HOST,DB_PORT,DB_NAME)
SQLALCHEMY_DATABASE_URI=DB_URI
SQLALCHEMY_TRACK_MODIFIER=False

3.app.py导入exts和config并初始化到app上

from flask import Flask
import config
from exts import db
app=Flask(__name__)#1.实例化app
app.config.from_object(config)#2.config配置文件绑定到app
db.init_app(app)#3.数据库绑定到app
@app.route('/')
def index():
    return "hello"
if __name__=="__main__":
    app.run()

2.创建用户模型并映射到数据库

1.models/auth.py创建用户模型

from exts import db
import shortuuid
from datetime import datetime
from werkzeug.security import generate_password_hash,check_password_hash


class UserModel(db.Model):
    __tablename__ = "user"
    id = db.Column(db.String(100), primary_key=True, default=shortuuid.uuid)
    email = db.Column(db.String(50), unique=True, nullable=False)
    username = db.Column(db.String(50), nullable=False)
    _password = db.Column(db.String(200), nullable=False)
    avatar = db.Column(db.String(100))
    signature = db.Column(db.String(100))
    join_time = db.Column(db.DateTime, default=datetime.now)
    is_staff = db.Column(db.Boolean, default=False)
    is_active = db.Column(db.Boolean, default=True)


    def __init__(self, *args, **kwargs):
        if "password" in kwargs:
            self.password = kwargs.get('password')
            kwargs.pop("password")
        super(UserModel, self).__init__(*args, **kwargs)

    @property
    def password(self):
        return self._password

    @password.setter
    def password(self, newpwd):
        self._password = generate_password_hash(newpwd)
    def check_password(self,rawpwd):
        return check_password_hash(self.password, rawpwd)

2.app.py导入模型并用flask-migrate管理数据库

from flask_migrate import Migrate
from models import auth
migrate=Migrate(app,db)

3.命令行migrate三部曲将模型映射到数据库

在app.py文件的目录下

flask db init
flask db migrate
flask db upgrade

3.登录与注册页面的get请求

1.首先写登录和注册的前端页面

#1。首先抽出base.html文件
<html>
<head>
    <meta charset="utf-8">
    <script src="http://cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script>
    <link href="http://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    <script src="http://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
    <script src="{{ url_for('static', filename='front/js/zlajax.js') }}"></script>
    <script src="{{ url_for('static', filename='front/js/zlparam.js') }}"></script>
    <link rel="stylesheet" href="{{ url_for('static', filename='front/css/front_base.css') }}">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>{% block title %}{% endblock %}</title>
    {% block head %}{% endblock %}
</head>

<body>
    <nav class="navbar navbar-default">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a class="navbar-brand" href="/">论坛</a>
            </div>
            <!-- Collect the nav links, forms, and other content for toggling -->
            <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                <ul class="nav navbar-nav">
                    <li class="active"><a href="/">首页<span class="sr-only">(current)</span></a></li>
                </ul>
                <form class="navbar-form navbar-left">
                    <div class="form-group">
                        <input type="text" class="form-control" placeholder="请输入关键字">
                    </div>
                    <button type="submit" class="btn btn-default">搜索</button>
                </form>
                <ul class="nav navbar-nav navbar-right">
                    {% if user %}
                      <li class="dropdown">
                          <a href="#" class="dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
                              {{ user.username }}
                              <span class="caret"></span>
                          </a>
                          <ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
                              <li><a href="{{ url_for('front.cms') }}">后台管理</a></li>
                              <li><a href="{{ url_for('front.setting') }}">设置</a></li>
                              <li><a href="{{ url_for('front.logout') }}">注销</a></li>
                          </ul>
                      </li>
                    {% else %}
                      <li><a href="{{ url_for('front.login') }}">登录</a></li>
                      <li><a href="{{ url_for('front.register') }}">注册</a></li>
                    {% endif %}
                </ul>
            </div><!-- /.navbar-collapse -->
        </div><!-- /.container-fluid -->
    </nav>
    <div class="main-container">
        {% block body %}{% endblock %}
    </div>
</body>

</html>
#2.login.html文件
{% extends "front/base.html" %}

{% block title %}
  登录
{% endblock %}

{% block head %}
  <link rel="stylesheet" href="{{ url_for('static', filename='front/css/signbase.css') }}">
  <script src="{{ url_for('static', filename='front/js/login.js') }}"></script>
{% endblock %}
{% block body %}
  <div class="outer-box">
    <div class="logo-box">
      <a href="/">
        <img src="{{ url_for('static', filename='front/images/logo.png') }}" alt="">
      </a>
    </div>
    <h2 class="page-title">
      登录
    </h2>
    <div class="sign-box">
      <div class="form-group">
        <input type="text" class="form-control" name="email" placeholder="邮箱">
      </div>
      <div class="form-group">
        <input type="password" class="form-control" name="password" placeholder="密码">
      </div>
      <div class="checkbox">
        <label>
          <input type="checkbox" name="remember" value="1">记住我
        </label>
      </div>
      <div class="form-group">
        <button class="btn btn-warning btn-block" id="submit-btn">立即登录</button>
      </div>
      <div class="form-group">
        <a href="#" class="signup-link">没有账号?立即注册</a>
        <a href="#" class="resetpwd-link" style="float:right;">找回密码</a>
      </div>
    </div>
  </div>
{% endblock %}
#3.register.html文件
{% extends "front/base.html" %}

{% block title %}
  注册
{% endblock %}

{% block head %}
  <link rel="stylesheet" href="{{ url_for('static', filename='front/css/signbase.css') }}">
  <script src="{{ url_for('static', filename='front/js/register.js') }}"></script>
{% endblock %}


{% block body %}
  <div class="outer-box">
            <div class="logo-box">
                <a href="/">
                    <img src="{{ url_for('static', filename='front/images/logo.png') }}" alt="">
                </a>
            </div>
            <h2 class="page-title">
                注册
            </h2>
            <div class="sign-box">
                <div class="form-group">
                    <div class="input-group">
                        <input type="email" class="form-control" name="email" placeholder="邮箱">
                        <span class="input-group-btn">
                            <button id="email-captcha-btn" class="btn btn-default">发送验证码</button>
                        </span>
                    </div>
                </div>
                <div class="form-group">
                    <input type="text" class="form-control" name="email-captcha" placeholder="邮箱验证码">
                </div>
                <div class="form-group">
                    <input type="text" class="form-control" name="username" placeholder="用户名">
                </div>
                <div class="form-group">
                    <input type="password" class="form-control" name="password" placeholder="密码">
                </div>
                <div class="form-group">
                    <input type="password" class="form-control" name="repeat-password" placeholder="确认密码">
                </div>
                <div class="form-group">
                    <div class="input-group">
                        <input type="text" class="form-control" name="graph-captcha" placeholder="图形验证码">
                        <span class="input-group-addon captcha-addon">
                            <img id="captcha-img" class="captcha-img" src="#" alt="">
                        </span>
                    </div>
                </div>
                <div class="form-group">
                    <button class="btn btn-warning btn-block" id="submit-btn">立即注册</button>
                </div>
            </div>
        </div>
{% endblock %}

2.写它们的view蓝图并导入到__init__中

#1.在apps/front/views.py里面写蓝图的视图函数
from flask import Blueprint,request,render_template
bp=Blueprint("front",__name__,url_prefix="/")

@bp.route('/login/', methods=['GET', 'POST'])
def login():
    if request.method == 'GET':
        return render_template('front/login.html')
    
@bp.route('/register/', methods=['GET', 'POST'])
def register():
    if request.method == 'GET':
        return render_template('front/register.html')
#2.在apps/front/__init__.py里面导入蓝图
from .views import bp as front_bp                             

3.蓝图注册到app

#在app.py里面导入蓝图并注册到app上
from apps.front import front_bp
app.register_blueprint(front_bp)

4.邮箱验证功能

1.邮箱验证

#1.在config里面配置邮箱第三方服务商来发送邮件
MAIL_SERVER="smtp.qq.com"#发送验证码的邮箱服务器,这里是自己公司的邮箱服务器
MAIL_PORT='587'#587是tls协议,465是ssl协议
MAIL_USE_TLS=True
#MAIL_USE_SSL
MAIL_USERNAME="1xxxc9@qq.com"
MAIL_PASSWORD="wxxbe"
MAIL_DEFAULT_SENDER="11xx@qq.com"
#2.exts里面导入mail
from flask_mail import Mail
mail=Mail()
#3.在app里面把exts里面的mail导入进来并绑定到app
from exts import db,mail
mail.init_app(app)
#4.开始在views里面写发送邮箱验证码的视图函数
from exts import mail
from flask_mail import Message
from flask importjsonify
import string,random
@bp.get("/email/captcha/")
def email_captcha():
    email=request.args.get('email')
    if not email:
        return jsonify({"code":400,"message":"请先传入邮箱"})
    source=list(string.digits)
    captcha="".join(random.sample(source,6))
    message=Message(subject="注册验证码",recipients=[email],body="您的注册验证码是:%s" % captcha)
    try:
        mail.send(message)
    except Exception as e:
        print("邮件发送失败")
        print(e)
        return jsonify({"code":500,"message":"邮件发送失败"})
    return jsonify({"code":200,"message":"邮件发送成功"})

2.使用celery异步发送邮箱验证网络请求

celery(分布式任务队列/任务调度器)和redis(内存数据库)的教程和安装步骤可以参考学习。Broker和Backend都用redis存储。

pip install gevent
pip install redis
pip install hiredis
启动celery

redis-cli

在这里插入图片描述

#1.在config中设置reids的相关信息
CELERY_BROKER_URL="redis://127.0.0.1:6379/0"#broker
CELERY_RESULT_BACKEND="redis://127.0.0.1:6379/0"#backend
#2.mycelery.py里面定义并添加任务
from flask_mail import Message
from exts import mail
from celery import Celery

# 定义任务函数
def send_mail(recipient,subject,body):
  message = Message(subject=subject,recipients=[recipient],body=body)
  try:
    mail.send(message)
    return {"status": "SUCCESS"}
  except Exception :
    return {"status": "FAILURE"}

# 创建celery对象
def make_celery(app):
  celery = Celery(app.import_name, backend=app.config['CELERY_RESULT_BACKEND'],broker=app.config['CELERY_BROKER_URL'])
  TaskBase = celery.Task

  class ContextTask(TaskBase):
    abstract = True

    def __call__(self, *args, **kwargs):
      with app.app_context():
        return TaskBase.__call__(self, *args, **kwargs)

  celery.Task = ContextTask
  app.celery = celery

  # 添加任务
  celery.task(name="send_mail")(send_mail)

  return celery
#3.在app.py里面将celery绑定到app
from mycelery import make_celery
mycelery=make_celery(app)
#4.在views.py里面利用current_app调用celery里面的task任务
from flask import current_app
@bp.get("/email/captcha/")
def email_captcha():
    email=request.args.get('email')
    if not email:
        return jsonify({"code":400,"message":"请先传入邮箱"})
    source=list(string.digits)
    captcha="".join(random.sample(source,6))
    subject="注册验证码"
    body="您的注册验证码是:%s"%captcha
    current_app.celery.send_task("send_mail",(email,subject,body))
    return jsonify({"code":200,"message":"邮件发送成功"})
#5.在工程目录下运行这个celery
celery -A app.mycelery worker --loglevel=info -P gevent
#6.访问这个视图函数就可以成功利用celery进行异步任务调取

3.使用flask-caching缓存验证码并验证

flask-caching的相关教程可以查看博文。

#1.安装:pip install flask-caching
#2.在config里面写flask-caching相关的配置
CACHE_TYPE="RedisCache"
CACHE_DEFAULT_TIMEOUT=300
CACHE_REDIS_HOST="127.0.0.1"
CACHE_REDIS_PORT=6379
#3.在exts里面引入caching
from flask_caching import Cache
cache=Cache()
#4.在app.py里面init
from exts import cache
cache.init_app(app)
#5.在view的视图函数里面缓存验证码
from exts import cache
cache.set(email,captcha)#cache缓存是键值对的形式

4.重构restful API

#1.在utils/restful.py里面
# Restful API
from flask import jsonify

class HttpCode(object):
  # 响应正常
  ok = 200
  # 没有登陆错误
  unloginerror = 401
  # 没有权限错误
  permissionerror = 403
  # 客户端参数错误
  paramserror = 400
  # 服务器错误
  servererror = 500

def _restful_result(code, message, data):
  return jsonify({ "code": code,"message": message or "", "data": data or {}})

def ok(message=None, data=None):
  return _restful_result(code=HttpCode.ok, message=message, data=data)

def unlogin_error(message="没有登录!"):
  return _restful_result(code=HttpCode.unloginerror, message=message, data=None)

def permission_error(message="没有权限访问!"):
  return _restful_result(code=HttpCode.paramserror, message=message, data=None)

def params_error(message="参数错误!"):
  return _restful_result(code=HttpCode.paramserror, message=message, data=None)

def server_error(message="服务器开小差啦!"):
  return _restful_result(code=HttpCode.servererror, message=message or '服务器内部错误', data=None)
#2.在view视图函数里
from utils import restful
return restful.params_error(message="请先传入邮箱")
return restful.ok(message="邮件发送成功")

5.注册页面的post请求

5.1注册页面邮箱验证码的ajax请求

#1.在register.html里面引入js文件
<script src="{{ url_for('static', filename='front/js/register.js') }}"></script>
#2.在register.js里面监听(4步),这里引用zlajax是因为它自动给了csrf-token
var RegisterHandler = function (){\\1.定义了一个JavaScript对象

}

RegisterHandler.prototype.listenSendCaptchaEvent = function (){\\2.包含一个方法
  var callback = function (event){
    // 原生的JS对象:this => jQuery对象
    var $this = $(this);
    // 阻止默认的点击事件
    event.preventDefault();
    var email = $("input[name='email']").val();
    var reg = /^\w+((.\w+)|(-\w+))@[A-Za-z0-9]+((.|-)[A-Za-z0-9]+).[A-Za-z0-9]+$/;
    if(!email || !reg.test(email)){
      alert("请输入正确格式的邮箱!");
      return;
    }
    zlajax.get({
      url: "/email/captcha?email=" + email,
      success: function (result){
        if(result['code'] == 200){
          console.log("邮件发送成功!");
          // 取消按钮的点击事件
          $this.off("click");
          // 添加禁用状态
          $this.attr("disabled", "disabled");
          // 开始倒计时
          var countdown = 60;
          var interval = setInterval(function (){
            if(countdown > 0){
              $this.text(countdown);
            }else{
              $this.text("发送验证码");
              $this.attr("disabled", false);
              $this.on("click", callback);
              // 清理定时器
              clearInterval(interval);
            }
            countdown--;
          }, 1000);
        }else{
          var message = result['message'];
          alert(message);
        }
      }
    })
  }
  $("#email-captcha-btn").on("click", callback);
}
RegisterHandler.prototype.run = function (){\\3.方法在run函数里调用
  this.listenSendCaptchaEvent();
}

// $(function(){})
$(function (){\\4.实例化并运行
  var handler = new RegisterHandler();
  handler.run();
})

#3.post请求要用csrf-token,所以在base.html里面引入
<meta name="csrf-token" content="{{csrf_token()}}">
#4.csrf-token需要先安装:
pip install flask-wtf
#5.在config里面设置secretkey
SECRET_KEY="FASDFNMLKSDF"
#6.在exts里面引入
from flask_wtf import CSRFProtect
csrf=CSRFProtect()
#6.在app上绑定init
from exts import csrf
csrf.init_app(app)

5.2注册页面的图形验证码功能

#1.在config里面获取工程的base目录
import os
BASE_DIR=os.path.dirname(__file__)
#2.在utils目录下的captcha的init文件里生成图形验证码
import random
import string
# Image:一个画布
# ImageDraw:一个画笔
# ImageFont:画笔的字体
from PIL import Image,ImageDraw,ImageFont

from flask import current_app
import os

# pip install pillow

# Captcha验证码

class Captcha(object):
    # 生成几位数的验证码
    number = 4
    # 验证码图片的宽度和高度
    size = (100,30)
    # 验证码字体大小
    fontsize = 25
    # 加入干扰线的条数
    line_number = 2

    # 构建一个验证码源文本
    SOURCE = list(string.ascii_letters)
    for index in range(0, 10):
        SOURCE.append(str(index))


    #用来绘制干扰线
    @classmethod
    def __gene_line(cls,draw,width,height):
        begin = (random.randint(0, width), random.randint(0, height))
        end = (random.randint(0, width), random.randint(0, height))
        draw.line([begin, end], fill = cls.__gene_random_color(),width=2)


    # 用来绘制干扰点
    @classmethod
    def __gene_points(cls,draw,point_chance,width,height):
        chance = min(100, max(0, int(point_chance))) # 大小限制在[0, 100]
        for w in range(width):
            for h in range(height):
                tmp = random.randint(0, 100)
                if tmp > 100 - chance:
                    draw.point((w, h), fill=cls.__gene_random_color())


    # 生成随机的颜色
    @classmethod
    def __gene_random_color(cls,start=0,end=255):
        random.seed()
        return (random.randint(start,end),random.randint(start,end),random.randint(start,end))


    # 随机选择一个字体
    @classmethod
    def __gene_random_font(cls):
        fonts = [
            'Courgette-Regular.ttf',
            'LHANDW.TTF',
            'Lobster-Regular.ttf',
            'verdana.ttf'
        ]
        font = random.choice(fonts)
        fontpath = os.path.join(current_app.config['BASE_DIR'],'utils','captcha',font)
        # return 'utils/captcha/'+font
        return fontpath


    # 用来随机生成一个字符串(包括英文和数字)
    @classmethod
    def gene_text(cls, number):
        # number是生成验证码的位数
        return ''.join(random.sample(cls.SOURCE, number))


    #生成验证码
    @classmethod
    def gene_graph_captcha(cls):
        # 验证码图片的宽和高
        width,height = cls.size
        # 创建图片
        # R:Red(红色)0-255
        # G:G(绿色)0-255
        # B:B(蓝色)0-255
        # A:Alpha(透明度)
        image = Image.new('RGBA',(width,height),cls.__gene_random_color(0,100))
        # 验证码的字体
        font = ImageFont.truetype(cls.__gene_random_font(),cls.fontsize)
        # 创建画笔
        draw = ImageDraw.Draw(image)
        # 生成字符串
        text = cls.gene_text(cls.number)
        # 获取字体的尺寸
        font_width, font_height = font.getsize(text)
        # 填充字符串
        draw.text(((width - font_width) / 2, (height - font_height) / 2),text,font= font,fill=cls.__gene_random_color(150,255))
        # 绘制干扰线
        for x in range(0, cls.line_number):
            cls.__gene_line(draw, width, height)
        # 绘制噪点
        cls.__gene_points(draw, 10, width, height)
        return (text,image)
#3.在views里面写视图函数
from utils.captcha import Captcha
import time
from hashlib import md5
from io import BytesIO
from flask import make_response
@bp.route("/graph/captcha/")
def graph_captcha():
    captcha,image=Captcha.gene_graph_captcha()
    key=md5((captcha+str(time.time())).encode('utf-8')).hexdigest()
    cache.set(key,captcha)#cache里面缓存这个captcha
    buffer=BytesIO()
    image.save(buffer,"png")
    buffer.seek(0)#buffer文件指针指向最开始的位置
    resp=make_response(buffer.read())
    resp.content_type="image/png"
    resp.set_cookie("_graph_captcha_key",key,max_age=3600)#将key值保存到cookie1个小时
    return resp
#4.在register.html里面写图片验证码的src
<img id="captcha-img" class="captcha-img" src="{{url_for('front.graph_captcha')}}" alt="">
#5.实现点击图片重新生成,所以在regist.js里面监听
RegisterHandler.prototype.listenGraphCaptchaEvent = function (){
  $("#captcha-img").on("click", function (){
    console.log("点击了图形验证码");
    var $this = $(this);
    var src = $this.attr("src");
    // /graph/captcha
    // /graph/captcha?sign=Math.random()
    // 防止一些老的浏览器,在两次url相同的情况下,不会重新发送请求,导致图形验证码不会更新
    let new_src = zlparam.setParam(src, "sign", Math.random())
    $this.attr("src",new_src);
  });
}
RegisterHandler.prototype.run = function (){
  this.listenSendCaptchaEvent();
  this.listenGraphCaptchaEvent();
}

5.3注册页面的post提交

#1.在front/forms.py里面进行表单验证
from wtforms import Form,ValidationError
from wtforms.fields import StringField
from wtforms.validators import Email,Length,EqualTo
from models.auth import UserModel#对表单进行二次验证
from exts import cache
from flask import request
class BaseForm(Form):
    @property
    def messages(self):
        message_list = []
        if self.errors:
            for error in self.errors.values():
                message_list.extend(error)
        return message_list
class RegisterForm(BaseForm):
    email=StringField(validators=[Email(message="请输入正确的邮箱")])
    email_captcha=StringField(validators=[Length(6,6,message="请输入6位验证码")])
    username=StringField(validators=[Length(3,20,message="请输入3-20位的用户名")])
    password=StringField(validators=[Length(6,20,message="请输入6-20位的密码")])    
    repeat_password=StringField(validators=[EqualTo("password",message="两次密码不一致")])
    graph_captcha=StringField(validators=[Length(4,4,message="请输入4位图形验证码")])
    def validate_email(self,field):
        email=field.data
        user=UserModel.query.filter_by(email=email).first()
        if user:
            raise ValidationError(message="邮箱已经被注册")
    def validate_email_captcha(self,field):
        email_captcha=field.data
        email=self.email.data
        cache_captcha=cache.get(email)
        if not cache_captcha or cache_captcha!=email_captcha:
            raise ValidationError(message="邮箱验证码错误")
    def validate_graph_captcha(self,field):
        graph_captcha=field.data
        key=request.cookies.get("_graph_captcha_key")
        cache_captcha=cache.get(key)
        if not cache_captcha or cache_captcha.lower()!=graph_captcha.lower():
            raise ValidationError(message="图形验证码错误")
#2.在front/views.py里面写post视图函数
from .forms import RegisterForm
from models.auth import UserModel
from exts import db
@bp.route('/register/', methods=['GET', 'POST'])
def register():
    if request.method == 'GET':
        return render_template('front/register.html')
    else:
        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=password)
            db.session.add(user)
            db.session.commit()
            return restful.ok()
        else:
            message=form.messages[0]
            return restful.params_error(message=message)
#3.在js里面绑定点击事件,跳到上面的视图函数
RegisterHandler.prototype.listenSubmitEvent = function (){
  $("#submit-btn").on("click", function (event){
    event.preventDefault();
    var email = $("input[name='email']").val();
    var email_captcha = $("input[name='email-captcha']").val();
    var username = $("input[name='username']").val();
    var password = $("input[name='password']").val();
    var repeat_password = $("input[name='repeat-password']").val();
    var graph_captcha = $("input[name='graph-captcha']").val();

    // 如果是商业项目,一定要先验证这些数据是否正确
    zlajax.post({
      url: "/register",
      data: {
        "email": email,
        "email_captcha": email_captcha,
        "username": username,
        password, // "password": password
        repeat_password,
        graph_captcha
      },
      success: function (result){
        if(result['code'] == 200){
          window.location = "/login";
        }else{
          alert(result['message']);
        }
      }
    })
  });
}

RegisterHandler.prototype.run = function (){
  this.listenSendCaptchaEvent();
  this.listenGraphCaptchaEvent();
  this.listenSubmitEvent();
}

6.登录页面的post请求

#1.首先表单验证
from wtforms.fields import IntegerField
class LoginForm(BaseForm):
    email=StringField(validators=[Email(message="请输入正确的邮箱")])
    password=StringField(validators=[Length(6,20,message="请输入6-20位的密码")]) 
    remember=IntegerField()
#2.视图函数
from flask import session
from .forms import LoginForm
@bp.route('/login/', methods=['GET', 'POST'])
def login():
    if request.method == 'GET':
        return render_template('front/login.html')
    else:
        form=LoginForm(request.form)
        if form.validate():
            email=form.email.data
            password=form.password.data
            remember=form.remember.data
            user=UserModel.query.filter_by(email=email).first()
            if not user:
                return restful.params_error("此邮箱没有注册")
            if not user.check_password(password):
                return restful.params_error("邮箱或密码错误")
            session['user_id']=user.id
            if remember ==1:
                session.permanent=True
            return restful.ok()
        else:
            return restful.params_error(message=form.messages[0])
#3.在config里面设置permanent时间
from datetime import timedelta
PERMANENT_SESSION_LIFETIME=timedelta(days=7)
#4.登录的post提交的前端监听,在login.js中,并加载到html中
var LoginHandler = function (){}

LoginHandler.prototype.listenSubmitEvent = function (){
  $("#submit-btn").on("click", function (event){
    event.preventDefault();
    var email = $("input[name='email']").val();
    var password = $("input[name='password']").val();
    var remember = $("input[name='remember']").prop("checked");
    zlajax.post({
      url: "/login",
      data: {
        email,
        password,
        remember: remember?1:0
      },
      success: function (result){
        if(result['code'] == 200){
          var token = result['data']['token'];
          var user = result['data']['user'];
          localStorage.setItem("JWT_TOKEN_KEY", token);
          localStorage.setItem("USER_KEY", JSON.stringify(user));
          window.location = "/"
        }else{
          alert(result['message']);
        }
      }
    })
  });
}

LoginHandler.prototype.run = function (){
  this.listenSubmitEvent();
}

$(function (){
  var handler = new LoginHandler();
  handler.run();
});

7.首页

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/818191.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

解决Debian10乱码以及远程连接ssh的问题

文章目录 解决Debian10乱码Debian10配置ssh 解决Debian10乱码 下载locales apt-get install locales配置语言 dpkg-reconfigure locales输入上述命令后会进入到以下页面【空格为选中&#xff0c;回车下一个页面】 在这个页面里我们按空格选中如图的选项&#xff0c;然后回…

安科瑞智慧空开微型断路器在银行的应用-安科瑞黄安南

应用场景 智能微型断路器与智能网关组合应用于末端回路 功能 1.计量功能&#xff1a;实时上报电压、电流、功率、电能、漏电、温度、频率等电参量&#xff1b; 2.报警功能&#xff1a;过压报警、欠压报警、过流报警、过载报警、漏电报警、超温报警、三相电缺相报警&#xff…

Jetson Docker 编译 FFmpeg 支持硬解nvmpi和cuvid

0 设备和docker信息 设备为NVIDIA Jetson Xavier NX&#xff0c;jetpack版本为 5.1.1 [L4T 35.3.1] 使用的docker镜像为nvcr.io/nvidia/l4t-ml:r35.2.1-py3,详见https://catalog.ngc.nvidia.com/orgs/nvidia/containers/l4t-ml 使用下列命令拉取镜像: sudo docker pull nvcr…

windows查看 jar包进程号指令

1 打开cmd 2 : 9898 jar包对应的端口号 netstat -aon|findstr 9898 3 &#xff1a;打开任务管理器 根据搜索出的23700 找到对应进程

【C++】STL——vector的模拟实现、常用构造函数、迭代器、运算符重载、扩容函数、增删查改

文章目录 1.模拟实现vector1.1构造函数1.2迭代器1.3运算符重载1.4扩容函数1.5增删查改 1.模拟实现vector vector使用文章 1.1构造函数 析构函数 在C中&#xff0c;vector是一个动态数组容器&#xff0c;可以根据需要自动调整大小。vector类提供了几个不同的构造函数来创建和初…

gradle项目上传项目依赖到远程仓库

gradle项目上传项目依赖到远程仓库 第一步&#xff1a;在需要上传的项目的bulid.gradle下添加maven插件&#xff0c;并配置连接远程仓库的信息以及项目的三要素信息&#xff0c;如下所示 dependencies {implementation org.mapstruct:mapstruct:1.4.2.Final } apply plugin: …

Linux - make/Makefifile

0.背景 会不会写makefile&#xff0c;从一个侧面说明了一个人是否具备完成大型工程的能力 一个工程中的源文件不计数&#xff0c;其按类型、功能、模块分别放在若干个目录中&#xff0c;makefile定义了一系列的规则来指定&#xff0c;哪些文件需要先编译&#xff0c;哪些文件需…

【Spring】Spring之循环依赖底层源码解析

什么是循环依赖 A依赖了B&#xff0c;B依赖了A。 示例&#xff1a; // A依赖了B class A{public B b; }// B依赖了A class B{public A a; }其实&#xff0c;循环依赖并不是问题&#xff0c;因为对象之间相互依赖是很正常的事情。示例&#xff1a; A a new A(); B b new B…

5分钟快手入门laravel邮件通知

第一步&#xff1a; 生成一个邮件发送对象 php artisan make:mail TestMail 第二步&#xff1a; 编辑.env 添加/修改&#xff08;没有的key则添加&#xff09; MAIL_DRIVERsmtp MAIL_HOSTsmtp.163.com &#xff08;这里用163邮箱&#xff09; MAIL_PORT25 &#xff08;163邮箱…

Bug记录: CUDA error_ device-side assert triggered

Bug记录&#xff1a; CUDA error: device-side assert triggered 在接触AIGC算法的过程中偶尔会遇到这样的bug&#xff1a;RuntimeError: CUDA error: device-side assert triggered return torch._C._cuda_synchronize() RuntimeError: CUDA error: device-side assert trig…

Qt实现引导界面UITour

介绍 最近做了一款键鼠自动化&#xff0c;想第一次安装打开后搞一个引导界面&#xff0c;找了好多资料没啥参考&#xff0c;偶然发现qt有引导界面如下图。 Qt整挺好&#xff0c;但是未找到源码&#xff0c;真的不想手撸&#xff0c;无奈实在找不到&#xff0c;下图是仿照qt实现…

在Vue中使用深度选择器定制Element Plus组件样式

介绍&#xff1a; 在Vue.js开发中&#xff0c;我们经常使用Element Plus作为UI组件库&#xff0c;它提供了丰富的组件供我们使用。然而&#xff0c;有时候我们希望对Element Plus的组件样式进行一些定制&#xff0c;比如调整字体大小、改变颜色等。在这篇博客中&#xff0c;我…

【GitOps系列】如何实施金丝雀发布?

文章目录 前言金丝雀发布概述金丝雀实战创建生产环境 部署金丝雀环境配置金丝雀策略金丝雀发布自动化创建 Rollout 对象创建 Service 和 Ingress 对象访问生产环境金丝雀发布自动化 访问 Argo Rollout Dashboard自动化原理结语 前言 蓝绿发布是一种通过资源冗余来换取回滚效率的…

关于jar文件反编译

最近在搞tck测试&#xff0c;想要将其日志转换成apdu脚本&#xff0c;结果出现默认输出最大长度不足&#xff0c;输出被省略现象。 软件log出现的错误信息 ... Output overflow: JavaTest Harness has limited the test output to the text to that at the beginning and the…

stable-diffusion-webui 启动服务,卡在浏览器loading中, 重定向解决

最新的code&#xff0c;按步骤安装&#xff0c;趟完pip和github的坑&#xff0c;终于启动服务 然后悲催的卡在浏览器这一步&#xff0c;一直在loading&#xff0c;折腾一下午&#xff0c;尝试可能有效的步骤&#xff0c;也许最后一步才有用&#xff1a; 1. 启动IIS服务 2. 配…

Java课题笔记~Maven基础

2、Maven 基础 2.1 Maven安装与配置 下载安装 配置&#xff1a;修改安装目录/conf/settings.xml 本地仓库&#xff1a;存放的是下载的jar包 中央仓库&#xff1a;要从哪个网站去下载jar包 - 阿里云的仓库 2.2 创建Maven项目

爬虫006_python中的运算符_算术运算符_赋值运算符_复合赋值运算符_比较运算符_逻辑运算符_逻辑运算符性能提升---python工作笔记024

首先看加减乘除 然后看这里的 // 是取整数部分,不是四舍五入 然后%这个是取余数 然后**是,几次方那种 指数

游游的排列构造

示例1 输入 5 2 输出 3 1 5 2 4 示例2 输入 5 3 输出 2 1 4 3 5 #include<bits/stdc.h> using namespace std; typedef long long ll; const int N1e55; int n,k; int main(){scanf("%d%d",&n,&k);int xn-k1;int yn-k;int f1;for(int i1;i&l…

八大排序算法--希尔排序(动图理解)

目录 希尔排序 概念 算法思路 动画演示 代码如下 复杂度分析 时间复杂度测试 运行结果 完整代码 创作不易&#xff0c;如果本篇博客对您有一定的帮助&#xff0c;大家记得留言点赞哦。 希尔排序 概念 希尔排序是插入排序的一种&#xff0c;是对直接插入排序的优化。其…

Docker基础命令(一)

Docker使用1 一、运行终端 打开终端&#xff0c;输入docker images &#xff0c;如果运行正常&#xff0c;表示docker已经可以在本电脑上使用了 二、docker常用命令 指令说明docker images查看已下载的镜像docker rmi 镜像名称:标签名删除已下载的镜像docker search 镜像从官…