1. 项目预览
1.1 登录
1.2 注册
2. 定制设计项目目录结构
3. 定制设计项目代码展示
blueprint
init.py
from .user import bp as user_bp
forms.py
import wtformsfrom wtforms.validators import length, email, EqualTofrom models import EmailCaptchaModel, UserModelclass LoginForm(wtforms.Form): email = wtforms.StringField(validators=[email()]) password = wtforms.StringField(validators=[length(min=6, max=20)])class RegisterForm(wtforms.Form): username = wtforms.StringField(validators=[length(min=3, max=20)]) email = wtforms.StringField(validators=[email()]) captcha = wtforms.StringField(validators=[length(min=4, max=4)]) password = wtforms.StringField(validators=[length(min=6, max=20)]) password_confirm = wtforms.StringField(validators=[EqualTo("password")]) def validate_captcha(self, field): captcha = field.data email = self.email.data captcha_model = EmailCaptchaModel.query.filter_by(email=email).first() if captcha_model.captcha.lower() != captcha.lower(): raise wtforms.ValidationError("定制设计邮箱验证码错误!") def validate_email(self, field): email = field.data user_model = UserModel.query.filter_by(email=email).first() if user_model: raise wtforms.ValidationError("邮箱已经存在!")
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
user.py
from flask import Blueprint, render_template, request, redirect, url_for, jsonify, session, flashfrom exts import mail, dbfrom flask_mail import Messagefrom models import EmailCaptchaModel, UserModelfrom datetime import datetimefrom .forms import RegisterForm, LoginFormfrom werkzeug.security import generate_password_hash, check_password_hashimport randomimport stringbp = Blueprint("user", __name__)@bp.route("/")def index(): return render_template("index.html")@bp.route("/login", methods=['GET', 'POST'])def login(): if request.method == 'GET': return render_template("login.html") else: form = LoginForm(request.form) if form.validate(): email = form.email.data password = form.password.data user = UserModel.query.filter_by(email=email).first() if user and check_password_hash(user.password, password): session['user_id'] = user.id return redirect("/") else: flash("邮箱和密码不匹配!!!") return redirect(url_for("user.login")) else: flash("邮箱或密码错误!!!") return redirect(url_for("user.login"))@bp.route("/register", methods=['GET', 'POST'])def register(): if request.method == 'GET': return render_template("register.html") else: form = RegisterForm(request.form) if form.validate(): email = form.email.data username = form.username.data password = form.password.data hash_password = generate_password_hash(password) user = UserModel(email=email, username=username, password=hash_password) db.session.add(user) db.session.commit() return redirect(url_for("user.login")) else: return redirect(url_for("user.register"))@bp.route("/logout")def logout(): session.clear() return redirect(url_for('user.login'))@bp.route("/captcha", methods=['POST'])def get_captcha(): email = request.form.get("email") letters = string.ascii_letters + string.digits captcha = "".join(random.sample(letters, 4)) if email: message = Message( subject='【验证码】', recipients=[email], body=f"【验证码】您的注册验证码是{captcha},请不要告诉任何人哦!" ) mail.send(message) captcha_model = EmailCaptchaModel.query.filter_by(email=email).first() if captcha_model: captcha_model.captcha = captcha captcha_model.create_time = datetime.now() db.session.commit() else: captcha_model = EmailCaptchaModel(email=email, captcha=captcha) db.session.add(captcha_model) db.session.commit() print("captcha:", captcha) return jsonify({"code": 200}) else: return jsonify({"code": 400, "message": "请先传递邮箱!"})
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
static
bootstrap@4.6.min.css
jquery.3.6.min.js
register.js
function bindCaptchaBtnClick(){ $("#captcha-btn").on("click",function (event){ var $this = $(this); var email = $("input[name='email']").val() if(!email){ alert("请先输入邮箱"); return; } $.ajax({ url: "/user/captcha", method: "POST", data: { "email": email }, success: function (res){ var code = res['code']; if (code == 200){ $this.off("click") var countDown = 60; var timer = setInterval(function (){ countDown -= 1; if (countDown > 0){ $this.text(countDown+"秒后重新发送"); }else { $this.text("获取验证码"); bindCaptchaBtnClick(); clearInterval(timer); } },1000) alert("验证码发送成功!"); }else{ alert(res['message']); } } }) });}$(function (){ bindCaptchaBtnClick();});
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
templates
base.html
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>{% block title %}{% endblock %}</title> <link rel="stylesheet" href="{{ url_for('static',filename='bootstrap/bootstrap@4.6.min.css') }}"> <link rel="stylesheet" href="{{ url_for('static',filename='css/css.css') }}"> {% block head %}{% endblock %}</head><body><nav class="navbar navbar-expand-lg navbar-light bg-light"> <div class="container"> <a class="navbar-brand" href="/"> <svg t="1659145665644" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6166" width="50" height="50"><path d="M512 512m-405.333333 0a405.333333 405.333333 0 1 0 810.666666 0 405.333333 405.333333 0 1 0-810.666666 0Z" fill="#4CAF50" p-id="6167"></path><path d="M640 426.666667c47.061333 0 85.333333 38.272 85.333333 85.333333s-38.272 85.333333-85.333333 85.333333-85.333333-38.272-85.333333-85.333333 38.272-85.333333 85.333333-85.333333m0-85.333334a170.666667 170.666667 0 1 0 0 341.333334 170.666667 170.666667 0 0 0 0-341.333334z" fill="#FFFFFF" p-id="6168"></path><path d="M384 426.666667c47.061333 0 85.333333 38.272 85.333333 85.333333s-38.272 85.333333-85.333333 85.333333-85.333333-38.272-85.333333-85.333333 38.272-85.333333 85.333333-85.333333m0-85.333334a170.666667 170.666667 0 1 0 0 341.333334 170.666667 170.666667 0 0 0 0-341.333334z" fill="#FFFFFF" p-id="6169"></path></svg> </a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarSupportedContent"> <ul class="navbar-nav mr-auto"> <li class="nav-item active"> <a class="nav-link" href="/">首页 <span class="sr-only">(current)</span></a> </li> </ul> <ul class="navbar-nav"> {% if user %} <li class="nav-item"> <span class="nav-link">{{ user.username }}</span> </li> <li class="nav-item"> <a class="nav-link" href="{{ url_for('user.logout') }}">退出登录</a> </li> {% else %} <li class="nav-item"> <a class="nav-link" href="{{ url_for('user.login') }}">登录</a> </li> <li class="nav-item"> <a class="nav-link" href="{{ url_for('user.register') }}">注册</a> </li> {% endif %} </ul> </div> </div></nav><div class="container">{% block body %}{% endblock %}</div></body></html>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
index.html
{% extends "base.html" %}{% block title %}首页{% endblock %}{% block head %}{% endblock %}{% block body %}<h1>文章首页</h1>{% endblock %}
login.html
{% extends "base.html" %}{% block title %}登录{% endblock %}{% block head %}{% endblock %}{% block body %} <div class="row mt-4"> <div class="col"></div> <div class="col"> <form action="{{ url_for("user.login") }}" method="post"> <div class="form-group"> <label for="exampleInputEmail1">邮箱</label> <input type="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" name="email"> </div> <div class="form-group"> <label for="exampleInputPassword1">密码</label> <input type="password" name="password" class="form-control" id="exampleInputPassword1"> </div> {% for message in get_flashed_messages() %} <div class="from-group"> <div class="text-danger">{{ message }}</div> </div> {% endfor %} <div class="from-group"> <button type="submit" class="btn btn-primary btn-block">立即登录</button> </div> </form> </div> <div class="col"></div> </div>{% endblock %}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
register.html
{% extends "base.html" %}{% block title %}注册{% endblock %}{% block head %} <script src="{{ url_for('static',filename='jquery/jquery.3.6.min.js') }}"></script> <script src="{{ url_for('static',filename='js/register.js') }}"></script>{% endblock %}{% block body %} <div class="row mt-4"> <div class="col"></div> <div class="col"> <form action="{{ url_for("user.register") }}" method="post"> <div class="form-group"> <label for="exampleInputEmail1">邮箱</label> <input type="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" name="email"> <small id="emailHelp" class="form-text text-muted">我们不会把邮箱用于其他用途</small> </div> <div class="form-group"> <label for="exampleInputEmail1">验证码</label> <div class="input-group"> <input type="text" class="form-control" name="captcha"> <div class="input-group-append"> <button class="btn btn-outline-secondary" type="button" id="captcha-btn">获取验证码</button> </div> </div> </div> <div class="form-group"> <label for="exampleInputEmail1">用户名</label> <input type="text" class="form-control" name="username"> </div> <div class="form-group"> <label for="exampleInputPassword1">密码</label> <input type="password" class="form-control" id="exampleInputPassword1" name="password"> </div> <div class="form-group"> <label for="exampleInputPassword1">确认密码</label> <input type="password" class="form-control" name="password_confirm"> </div> <button type="submit" class="btn btn-primary btn-block">立即注册</button> </form> </div> <div class="col"></div> </div>{% endblock %}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
app.py
from flask import Flask, session, gimport configfrom exts import db, mailfrom blueprints import user_bpfrom flask_migrate import Migratefrom models import UserModelapp = Flask(__name__)app.config.from_object(config)db.init_app(app)mail.init_app(app)migrate = Migrate(app, db)app.register_blueprint(user_bp)@app.before_requestdef before_request(): user_id = session.get("user_id") if user_id: try: user = UserModel.query.get(user_id) g.user = user except: g.user = None@app.context_processordef context_processor(): if hasattr(g, "user"): return {"user": g.user} else: return {}if __name__ == '__main__': app.run()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
config.py
HOSTNAME = '127.0.0.1' PORT = '3306' DATABASE = '数据库名'USERNAME = '数据库账号'PASSWORD = '数据库密码'DB_URI = 'mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)SQLALCHEMY_DATABASE_URI = DB_URISQLALCHEMY_TRACK_MODIFICATIONS = TrueSECRET_KEY = "sdasd54as56d4a65s4"MAIL_SERVER = "smtp.qq.com"MAIL_PORT = 465MAIL_USE_TLS = FalseMAIL_USE_SSL = TrueMAIL_DEBUG = TrueMAIL_USERNAME = "发件邮箱"MAIL_PASSWORD = "授权码"MAIL_DEFAULT_SENDER = "默认发件邮箱"
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
decorators.py
from flask import g, redirect, url_forfrom functools import wrapsdef login_required(func): @wraps(func) def wrapper(*args,**kwargs): if hasattr(g,'user'): return func(*args,**kwargs) else: return redirect(url_for("user.login")) return wrapper
exts.py
from flask_sqlalchemy import SQLAlchemyfrom flask_mail import Maildb = SQLAlchemy()mail = Mail()
models.py
from exts import dbfrom datetime import datetimeclass EmailCaptchaModel(db.Model): __tablename__ = "email_captcha" id = db.Column(db.Integer, primary_key=True, autoincrement=True) email = db.Column(db.String(100), nullable=False, unique=True) captcha = db.Column(db.String(10), nullable=False) creat_time = db.Column(db.DateTime, default=datetime.now)class UserModel(db.Model): __tablename__ = "user" id = db.Column(db.Integer, primary_key=True, autoincrement=True) username = db.Column(db.String(200), nullable=False, unique=True) email = db.Column(db.String(100), nullable=False, unique=True) password = db.Column(db.String(200), nullable=False) join_time = db.Column(db.DateTime, default=datetime.now)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20