python+mysql+bootstrap条件搜索分页
支持登录和退出,使用session或者redis(默认)
先看效果


1项目目录

2表字段
CREATE TABLE `user` (
`id` INT ( 11 ) NOT NULL AUTO_INCREMENT,
`username` VARCHAR ( 255 ) CHARACTER
SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`password` VARCHAR ( 255 ) CHARACTER
SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`image` VARCHAR ( 255 ) DEFAULT NULL,
`sex` INT ( 11 ) DEFAULT NULL,
`remark` VARCHAR ( 255 ) CHARACTER
SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`create_time` datetime DEFAULT NULL,
PRIMARY KEY ( `id` )
) ENGINE = MyISAM AUTO_INCREMENT = 38 DEFAULT CHARSET = utf8;
3requirements.txt
blinker==1.9.0
click==8.3.2
colorama==0.4.6
DBUtils==3.1.2
Flask==3.1.3
itsdangerous==2.2.0
Jinja2==3.1.6
MarkupSafe==3.0.3
PyMySQL==1.1.2
redis==7.4.0
Werkzeug==3.1.8
4add.html
{% extends 'layout.html' %}
{% block body %}
<form method="post" >
<h1 style="align-content: center;text-align: center;">用户添加</h1>
<div class="form-group">
<label class="col-sm-2 col-form-label">用户名</label>
<div class="col-sm-10">
<input type="text" class="form-control" name="username">
</div>
</div>
<br>
<div class="form-group">
<label class="col-sm-2 col-form-label">密码</label>
<div class="col-sm-10">
<input type="password" class="form-control" name="password">
</div>
</div><br>
<div class="form-group">
<label class="col-sm-2 col-form-label">图片</label>
<div class="col-sm-10">
<input type="text" class="form-control" name="image">
</div>
</div><br>
<div class="form-group">
<label class="col-sm-2 col-form-label">性别</label>
<div class="col-sm-10">
<label class="radio-inline">
<input type="radio" name="sex" value="1"> 男
</label>
<label class="radio-inline">
<input type="radio" name="sex" value="2"> 女
</label>
</div>
</div><br>
<div class="form-group">
<label class="col-sm-2 col-form-label">备注</label>
<div class="col-sm-10">
<textarea class="form-control" rows="3" name="remark"></textarea>
</div>
</div><br>
<button type="submit" class="btn btn-success" style="text-align: center;margin: auto;">添加</button>
</form>
{% endblock %}
5edit.html
{% extends 'layout.html' %}
{% block body %}
<form method="post" action="/user/editAction">
<h1 style="align-content: center;text-align: center;">用户编辑</h1>
<input type="hidden" value="{{one['id']}}" name="id">
<input type="hidden" name="search" value="{{ request.args.get('search', '') }}">
<input type="hidden" name="page" value="{{ request.args.get('page', '1') }}">
<div class="form-group">
<label class="col-sm-2 col-form-label">用户名</label>
<div class="col-sm-10">
<input type="text" class="form-control" name="username" value="{{one['username']}}">
</div>
</div>
<br>
<div class="form-group">
<label class="col-sm-2 col-form-label">密码</label>
<div class="col-sm-10">
<input type="password" class="form-control" name="password" value="{{one['password']}}">
</div>
</div><br>
<div class="form-group">
<label class="col-sm-2 col-form-label">图片</label>
<div class="col-sm-10">
<input type="text" class="form-control" name="image" value="{{one['image']}}">
</div>
</div><br>
<div class="form-group">
<label class="col-sm-2 col-form-label">性别</label>
<div class="col-sm-10">
<label class="radio-inline">
<input type="radio" name="sex" value="1" {% if one['sex'] == 1 %}checked{% endif %}> 男
</label>
<label class="radio-inline">
<input type="radio" name="sex" value="2" {% if one['sex'] == 2 %}checked{% endif %}> 女
</label>
</div>
</div>
<br>
<div class="form-group">
<label class="col-sm-2 col-form-label">备注</label>
<div class="col-sm-10">
<textarea class="form-control" rows="3" name="remark">{{one['remark']}}</textarea>
</div>
</div><br>
<button type="submit" class="btn btn-success" style="text-align: center;margin: auto;">确认编辑</button>
</form>
{% endblock %}
6layout.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="/static/bootstrap-3.4.1/css/bootstrap.css">
</head>
<body>
<div >
<nav class="navbar navbar-default">
<div class="container">
<!-- Brand and toggle get grouped for better mobile display -->
<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="#">xx用户管理平台</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><a href="#">用户管理</a></li>-->
<li><a href="/user/list">用户列表</a></li>
<!-- <li class="dropdown">-->
<!-- <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a>-->
<!-- <ul class="dropdown-menu">-->
<!-- <li><a href="#">Action</a></li>-->
<!-- <li><a href="#">Another action</a></li>-->
<!-- <li><a href="#">Something else here</a></li>-->
<!-- <li role="separator" class="divider"></li>-->
<!-- <li><a href="#">Separated link</a></li>-->
<!-- <li role="separator" class="divider"></li>-->
<!-- <li><a href="#">One more separated link</a></li>-->
<!-- </ul>-->
<!-- </li>-->
</ul>
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{{get_real_name()}} <span class="caret"></span></a>
<ul class="dropdown-menu">
<!-- <li><a href="#">Action</a></li>-->
<!-- <li><a href="#">Another action</a></li>-->
<!-- <li><a href="#">Something else here</a></li>-->
<!-- <li role="separator" class="divider"></li>-->
<li><a href="/logout">退出</a></li>
</ul>
</li>
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
<div class="container">
{%block body%}{%endblock%}
</div>
</div>
<script src="/static/jquery-3.4.1.min.js"></script>
<script src="/static/bootstrap-3.4.1/js/bootstrap.js"></script>
</body>
</html>
7list.html
{% extends 'layout.html' %}
{% block body %}
<div class="row" style="margin-bottom: 15px;">
{# 搜索框:占据左侧区域 #}
<div class="col-md-6">
<form class="navbar-form navbar-left" role="search" method="get" action="/user/list" style="margin:0; padding:0;">
<div class="form-group">
<input type="text" class="form-control" placeholder="按用户名搜索" name="search" value='{{ search }}'>
</div>
<button type="submit" class="btn btn-default">搜索</button>
</form>
</div>
{# 添加按钮:占据右侧区域,并用 text-right 推到最右边 #}
<div class="col-md-6 text-right">
<div class="btn btn-success" style="display: inline-block;">
<a class="btn btn-success" href="/user/add">添加</a>
</div>
</div>
</div>
<table class="table table-bordered" border="1" style="margin: auto;">
<thead>
<th>ID</th>
<th>用户名称</th>
<th>密码</th>
<th>性别</th>
<th>图片</th>
<th>备注</th>
<th>创建时间</th>
<th>操作</th>
</thead>
<tbody>
{% for item in data_list %}
<tr>
<td>{{ item.id }}</td>
<td>{{ item.username }}</td>
<td>{{ item.password }}</td>
<td>{{ sex_dict[item.sex] }}</td>
<td>{{ item.image }}</td>
<td>{{ item.remark }}</td>
<td>{{ item.create_time }}</td>
<td>
<a href="/user/delete?id={{ item.id }}&search={{ search }}&page={{ page }}">删除</a>
<a href="/user/edit?id={{ item.id }}&search={{ search }}&page={{ page }}">修改</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{# ========== 分页代码 ========== #}
<nav aria-label="Page navigation" style="text-align: center;">
<ul class="pagination" style="display: inline-block;">
{# 上一页 #}
{% if page == 1 %}
<li class="disabled">
<a href="javascript:void(0);" aria-label="Previous">
<span aria-hidden="true"><<</span>
</a>
</li>
{% else %}
<li>
{% set prev_url = '/user/list?page=' ~ (page - 1) ~ '&size=' ~ size ~ ('&search=' ~ search if search else '') %}
<a href="{{ prev_url }}" aria-label="Previous">
<span aria-hidden="true"><<</span>
</a>
</li>
{% endif %}
{# 页码 #}
{% for p in range(1, total_pages + 1) %}
{% if p == page %}
<li class="active"><a href="javascript:void(0);">{{ p }}</a></li>
{% else %}
<li>
{% set page_url = '/user/list?page=' ~ p ~ '&size=' ~ size ~ ('&search=' ~ search if search else '') %}
<a href="{{ page_url }}">{{ p }}</a>
</li>
{% endif %}
{% endfor %}
{# 下一页 #}
{% if page == total_pages %}
<li class="disabled">
<a href="javascript:void(0);" aria-label="Next">
<span aria-hidden="true">>></span>
</a>
</li>
{% else %}
<li>
{% set next_url = '/user/list?page=' ~ (page + 1) ~ '&size=' ~ size ~ ('&search=' ~ search if search else '') %}
<a href="{{ next_url }}" aria-label="Next">
<span aria-hidden="true">>></span>
</a>
</li>
{% endif %}
</ul>
</nav>
{% endblock %}
8login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="/static/bootstrap-3.4.1/css/bootstrap.css">
<script src="/static/jquery-3.4.1.min.js"></script>
<script src="/static/bootstrap-3.4.1/js/bootstrap.js"></script>
</head>
<body>
<div style="align-content: center;margin-top: 100px;margin-left: auto;margin-right: auto;width: 600px;border:1px solid #ddd;padding: 30px;">
<form method="post" >
<h1 style="align-content: center;text-align: center;">用户登录</h1>
<div class="form-group">
<label class="col-sm-2 col-form-label">用户名</label>
<div class="col-sm-10">
<input type="text" class="form-control" name="username">
</div>
</div>
<br>
<div class="form-group">
<label class="col-sm-2 col-form-label">密码</label>
<div class="col-sm-10">
<input type="password" class="form-control" name="password">
</div>
</div><br>
<button type="submit" class="btn btn-primary" style="text-align: center;margin: auto;">登录</button>
<span style="color:red;">{{ request.args.get('error', '') }}</span>
</form>
</div>
</body>
</html>
from flask import Blueprint, render_template,request,redirect,session,make_response
from utils import db
# 导入我们刚写的Redis工具方法
from utils.cache import save_login_user_to_redis,delete_login_user_from_redis
# 蓝图名字必须叫 login_bp
login_bp = Blueprint("login", __name__)
#session登录版==================================================================
# @login_bp.route("/login",methods=["GET","POST"])
# def login():
# if request.method == "GET":
# return render_template("login.html")
#
# username = request.form.get("username")
# password = request.form.get("password")
#
# user_dict = db.fetchone("select * from user where username=%s and password=%s ",(username,password))
# if user_dict:
# session["user_info"] = {'real_name':user_dict["username"]}
# return redirect("/user/list")
# else:
# return redirect("/login?error=用户名或密码错误")
# @login_bp.route("/logout")
# def logout():
# # 清除 session 中保存的用户信息
# session.pop("user_info", None) # 推荐写法:只删除 user_info,不影响其他 session
# # 或者直接清空所有 session(二选一即可)
# # session.clear()
# return redirect("/login")
#redis登录版================================================================================
@login_bp.route("/login",methods=["GET","POST"])
def login():
if request.method == "GET":
return render_template("login.html")
# POST提交登录表单
username = request.form.get("username")
password = request.form.get("password")
# 原来的数据库查询完全不变
user_dict = db.fetchone("select * from user where username=%s and password=%s ",(username,password))
if user_dict:
# 1. 登录成功:把用户信息存入Redis,获取登录token
login_token = save_login_user_to_redis(user_dict)
# 2. 重定向到首页,同时把token写入浏览器Cookie(前端后续请求会自动携带)
response = make_response(redirect("/user/list"))
# Cookie有效期30分钟,和Redis过期时间保持一致
response.set_cookie("login_token", login_token, max_age=1800)
return response
else:
return redirect("/login?error=用户名或密码错误")
# 退出登录接口同步改成Redis版
@login_bp.route("/logout")
def logout():
# 1. 从Cookie取出token
login_token = request.cookies.get("login_token")
# 2. 删除Redis里的登录信息
if login_token:
delete_login_user_from_redis(login_token)
# 3. 清除浏览器Cookie里的token,重定向到登录页
response = make_response(redirect("/login"))
response.delete_cookie("login_token")
return response
from flask import Blueprint, session, redirect,render_template,request
from utils import db
from datetime import datetime
from utils.cache import get_login_user_from_redis
import math
from utils import cache
# 蓝图名字必须叫 od(和视频一致)
user_bp = Blueprint("user", __name__)
import math
#session版========================================================================
# @user_bp.route("/user/list")
# def user_list():
# user_info = session.get("user_info")
# if not user_info:
# return redirect("/login")
#
# # 1. 获取搜索词和分页参数
# search = request.args.get("search", "").strip() # 获取搜索词,并去除首尾空格
# page = int(request.args.get("page", 1))
# size = int(request.args.get("size", 3))
#
# # 2. 拼接 SQL 和参数(根据是否有搜索词动态生成)
# if search:
# # 有搜索词:使用 LIKE 模糊查询
# count_sql = "SELECT COUNT(*) as total FROM `user` WHERE username LIKE %s"
# data_sql = "SELECT * FROM `user` WHERE username LIKE %s order by create_time desc LIMIT %s, %s "
# # %s 不能直接写 '%张%',必须把 '%张%' 作为一个整体传给 params
# like_str = f"%{search}%"
# total_count = db.userCount(count_sql, [like_str])
# start_index = (page - 1) * size
# data_list = db.fetchAll(data_sql, [like_str, start_index, size])
# else:
# # 无搜索词:查询全部
# count_sql = "SELECT COUNT(*) as total FROM `user`"
# data_sql = "SELECT * FROM `user` order by create_time desc LIMIT %s, %s"
# total_count = db.userCount(count_sql, [])
# start_index = (page - 1) * size
# data_list = db.fetchAll(data_sql, [start_index, size])
#
# # 3. 计算总页数
# total_pages = math.ceil(total_count / size) if total_count > 0 else 1
#
# # 4. 字典
# sex_dict = {1: "男", 2: "女"}
#
# # 5. 返回模板(注意把 search 也传回前端)
# return render_template(
# "list.html",
# data_list=data_list,
# sex_dict=sex_dict,
# page=page,
# size=size,
# total_pages=total_pages,
# search=search # 传给前端,用于回显和拼接分页链接
# )
# @user_bp.route("/user/add",methods=["GET","POST"])
# def user_create():
# user_info = session.get("user_info")
# if not user_info:
# return redirect("/login")
# if request.method == "GET":
# return render_template("add.html")
# else:
# username = request.form.get("username")
# password = request.form.get("password")
# image = request.form.get("image")
# sex = request.form.get("sex")
# remark = request.form.get("remark")
# create_time = datetime.now()
# params = [username,password,image,sex,remark,create_time]
# db.execute("insert into `user` (username,password,image,sex,remark,create_time) values(%s,%s,%s,%s,%s,%s)",params)
# return redirect("/user/list")
#
# @user_bp.route("/user/delete")
# def delete_create():
# user_info = session.get("user_info")
# if not user_info:
# return redirect("/login")
#
# # 1. 获取编辑页面传过来的 搜索词 和 页码
# search = request.args.get("search", "")
# page = request.args.get("page", "1")
# id = request.args.get("id")
# params = [id]
# db.execute("delete from `user` where id=%s", params)
# return redirect(f"/user/list?page={page}&size=3&search={search}")
# @user_bp.route("/user/editAction", methods=["GET", "POST"])
# def edit_action():
# user_info = session.get("user_info")
# if not user_info:
# return redirect("/login")
#
# id = int(request.form.get("id"))
# username = request.form.get("username")
# password = request.form.get("password")
# image = request.form.get("image")
# sex = request.form.get("sex")
# remark = request.form.get("remark")
#
# # 1. 获取编辑页面传过来的 搜索词 和 页码
# search = request.form.get("search", "")
# page = request.form.get("page", "1")
#
# params = [username, password, image, sex, remark, id]
# db.execute("update `user` set username=%s, password=%s, image=%s, sex=%s, remark=%s where id=%s", params)
#
# # 2. 重定向时把 search 和 page 拼回去
# return redirect(f"/user/list?page={page}&size=3&search={search}")
# @user_bp.route("/user/edit")
# def edit():
# user_info = session.get("user_info")
# if not user_info:
# return redirect("/login")
#
# id = request.args.get("id")
# params = [id]
# # 查询用户数据
# one = db.fetchone("select * from `user` where id=%s ", params)
# # 状态字典
# sex_dict = {
# 1: "男",
# 2: "女"
# }
# # 返回模板
# return render_template("edit.html", one=one, sex_dict=sex_dict)
#
#redis版==========================================================================
# 从Cookie获取登录token
@user_bp.route("/user/list")
def user_list():
# 从Cookie获取登录token
login_token = request.cookies.get("login_token")
# 从Redis查询用户信息,校验是否登录
login_username = get_login_user_from_redis(login_token)
# 未登录→强制跳登录页
if not login_username:
return redirect("/login")
# 1. 获取搜索词和分页参数
search = request.args.get("search", "").strip() # 获取搜索词,并去除首尾空格
page = int(request.args.get("page", 1))
size = int(request.args.get("size", 3))
# 2. 拼接 SQL 和参数(根据是否有搜索词动态生成)
if search:
# 有搜索词:使用 LIKE 模糊查询
count_sql = "SELECT COUNT(*) as total FROM `user` WHERE username LIKE %s"
data_sql = "SELECT * FROM `user` WHERE username LIKE %s order by create_time desc LIMIT %s, %s "
# %s 不能直接写 '%张%',必须把 '%张%' 作为一个整体传给 params
like_str = f"%{search}%"
total_count = db.userCount(count_sql, [like_str])
start_index = (page - 1) * size
data_list = db.fetchAll(data_sql, [like_str, start_index, size])
else:
# 无搜索词:查询全部
count_sql = "SELECT COUNT(*) as total FROM `user`"
data_sql = "SELECT * FROM `user` order by create_time desc LIMIT %s, %s"
total_count = db.userCount(count_sql, [])
start_index = (page - 1) * size
data_list = db.fetchAll(data_sql, [start_index, size])
# 3. 计算总页数
total_pages = math.ceil(total_count / size) if total_count > 0 else 1
# 4. 字典
sex_dict = {1: "男", 2: "女"}
# 5. 返回模板(注意把 search 也传回前端)
return render_template(
"list.html",
data_list=data_list,
sex_dict=sex_dict,
page=page,
size=size,
total_pages=total_pages,
search=search #
)
@user_bp.route("/user/add",methods=["GET","POST"])
def user_create():
# 从Cookie获取登录token
login_token = request.cookies.get("login_token")
# 从Redis查询用户信息,校验是否登录
login_username = get_login_user_from_redis(login_token)
# 未登录→强制跳登录页
if not login_username:
return redirect("/login")
if request.method == "GET":
return render_template("add.html")
else:
username = request.form.get("username")
password = request.form.get("password")
image = request.form.get("image")
sex = request.form.get("sex")
remark = request.form.get("remark")
create_time = datetime.now()
params = [username,password,image,sex,remark,create_time]
db.execute("insert into `user` (username,password,image,sex,remark,create_time) values(%s,%s,%s,%s,%s,%s)",params)
return redirect("/user/list")
@user_bp.route("/user/delete")
def delete_create():
# 从Cookie获取登录token
login_token = request.cookies.get("login_token")
# 从Redis查询用户信息,校验是否登录
login_username = get_login_user_from_redis(login_token)
# 未登录→强制跳登录页
if not login_username:
return redirect("/login")
# 1. 获取编辑页面传过来的 搜索词 和 页码
search = request.args.get("search", "")
page = request.args.get("page", "1")
id = request.args.get("id")
params = [id]
db.execute("delete from `user` where id=%s", params)
return redirect(f"/user/list?page={page}&size=3&search={search}")
@user_bp.route("/user/editAction", methods=["GET", "POST"])
def edit_action():
# 从Cookie获取登录token
login_token = request.cookies.get("login_token")
# 从Redis查询用户信息,校验是否登录
login_username = get_login_user_from_redis(login_token)
# 未登录→强制跳登录页
if not login_username:
return redirect("/login")
id = int(request.form.get("id"))
username = request.form.get("username")
password = request.form.get("password")
image = request.form.get("image")
sex = request.form.get("sex")
remark = request.form.get("remark")
# 1. 获取编辑页面传过来的 搜索词 和 页码
search = request.form.get("search", "")
page = request.form.get("page", "1")
params = [username, password, image, sex, remark, id]
db.execute("update `user` set username=%s, password=%s, image=%s, sex=%s, remark=%s where id=%s", params)
# 2. 重定向时把 search 和 page 拼回去
return redirect(f"/user/list?page={page}&size=3&search={search}")
@user_bp.route("/user/edit")
def edit():
# 从Cookie获取登录token
login_token = request.cookies.get("login_token")
# 从Redis查询用户信息,校验是否登录
login_username = get_login_user_from_redis(login_token)
# 未登录→强制跳登录页
if not login_username:
return redirect("/login")
id = request.args.get("id")
params = [id]
# 查询用户数据
one = db.fetchone("select * from `user` where id=%s ", params)
# 状态字典
sex_dict = {
1: "男",
2: "女"
}
# 返回模板
return render_template("edit.html", one=one, sex_dict=sex_dict)
11__init__.py
from flask import Flask,request,session,redirect
# 导入你 cache.py 里的 Redis 校验方法
from utils.cache import get_login_user_from_redis
#session版本=================================================
# def auth():
# print(request.path)
# if request.path.startswith('/static'):
# return
#
# if request.path == '/login':
# #继续向下执行
# return
# else:
# user_info = session.get("user_info")
# if user_info:
# return
# else:
# return redirect("/login")
# def get_real_name():
# user_info = session.get("user_info")
# return user_info['real_name']
# #使用redis========================================
def auth():
print(request.path)
if request.path.startswith('/static'):
return
if request.path == '/login':
#继续向下执行
return
else:
login_token = request.cookies.get("login_token")
login_username = get_login_user_from_redis(login_token)
if login_username:
return
else:
return redirect("/login")
#redis版本
def get_real_name():
login_token = request.cookies.get("login_token")
real_name = get_login_user_from_redis(login_token)
return real_name
def create_app():
app = Flask(__name__)
# app.secret_key = 'asdasdasda;' # 现在已经不用 session 了,可留可删
from .views import login
from .views import user
app.register_blueprint(login.login_bp)
app.register_blueprint(user.user_bp)
app.before_request(auth)
# 模板全局函数(页面里直接用 {{ get_real_name() }})
app.template_global()(get_real_name)
return app
#普通登录存到session和cookie
# 1浏览器每次请求 → 自动带上这个 session Cookie
# 2服务器拿到 Cookie → 解密 → 得到 user_info
# 3服务器判断:你已登录
# 为什么由session改成redis
# 1登录流程:账号密码校验通过 → 生成唯一 Token → Token + 用户信息存入 Redis(带过期)→ Token 写入浏览器 Cookie
# 2访问流程:前端请求接口 → 自动携带 Cookie 里的 Token → 后端从 Redis 查 Token 是否有效 → 有效则放行,无效跳登录
# 3退出流程:删除 Redis 里的 Token → 删除浏览器 Cookie → 彻底登出
# 细节问题:
# Redis key 前缀login:token:,和你原来的队列 key 完全隔离,不会冲突
# Token 用 uuid 全局唯一,不会重复、不会被猜解
# Redis 自动过期 + 访问自动续期,闲置 30 分钟自动登出
# 只存用户名,不存明文密码到 Redis,更安全
import redis
import uuid
# Redis连接池配置(和你原来配置完全一致)
POOL = redis.ConnectionPool(
host="127.0.0.1",
port=6379,
password="123456",
encoding="utf-8",
decode_responses=True, # 自动解码bytes→字符串,不用手动转码
max_connections=1000
)
# 获取Redis连接对象
def get_redis_conn():
return redis.Redis(connection_pool=POOL)
# ========== 以下是登录专用Redis方法 ==========
# 登录成功:生成唯一Token,存入Redis,返回Token
def save_login_user_to_redis(user_info_dict):
"""
user_info_dict:数据库查询出来的用户字典(user_dict)
返回:登录token字符串
"""
conn = get_redis_conn()
# 生成全局唯一登录令牌token
login_token = str(uuid.uuid4())
# Redis存储用户信息,过期时间30分钟(1800秒),可自行修改
conn.setex(
name=f"login:token:{login_token}",
time=1800,
value=user_info_dict["username"] # 保存登录用户名,和你原来session逻辑一致
)
return login_token
# 根据token从Redis获取登录用户信息
def get_login_user_from_redis(token):
conn = get_redis_conn()
key = f"login:token:{token}"
username = conn.get(key)
# 续期:每次访问自动延长30分钟有效期(可选优化)
if username:
conn.expire(key, 1800)
return username
# 退出登录:删除Redis里的token
def delete_login_user_from_redis(token):
conn = get_redis_conn()
conn.delete(f"login:token:{token}")
# 你原来的队列方法保留不动
def push_queue(value):
conn = get_redis_conn()
conn.lpush("day20260415_task_queue",value)
import pymysql
from pymysql import cursors
from dbutils.pooled_db import PooledDB
POOL = PooledDB(
creator=pymysql,
maxconnections=10,
mincached=2,
maxcached=5,
blocking=True,
setsession=[],
ping=0,
host='localhost',
port=3306,
user='root',
password='root',
db='test',
charset='utf8'
)
def fetchone(sql,param):
conn = POOL.connection()
cursor = conn.cursor(cursor=cursors.DictCursor)
cursor.execute(sql,param)
result = cursor.fetchone()
cursor.close()
conn.close()
return result
def fetchAll(sql,param):
conn = POOL.connection()
cursor = conn.cursor(cursor=cursors.DictCursor)
cursor.execute(sql,param)
result = cursor.fetchall()
cursor.close()
conn.close()
return result
def execute(sql,param):
conn = POOL.connection()
cursor = conn.cursor(cursor=cursors.DictCursor)
cursor.execute(sql,param)
conn.commit()
cursor.close()
conn.close()
return cursor.lastrowid
def userCount(sql, param):
"""
新增:查询数据条数(返回 int 数字)
使用示例:count("select count(*) as num from `user` where status=%s", [1])
"""
conn = POOL.connection()
cursor = conn.cursor(cursor=cursors.DictCursor)
cursor.execute(sql, param)
result = cursor.fetchone()
# 取出第一条数据的第一个值,返回 int 类型
num = result.get(list(result.keys())[0]) if result else 0
cursor.close()
conn.close()
return num
"""
CREATE TABLE `user` (
`id` INT ( 11 ) NOT NULL AUTO_INCREMENT,
`username` VARCHAR ( 255 ) CHARACTER
SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`password` VARCHAR ( 255 ) CHARACTER
SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`image` VARCHAR ( 255 ) DEFAULT NULL,
`sex` INT ( 11 ) DEFAULT NULL,
`remark` VARCHAR ( 255 ) CHARACTER
SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`create_time` datetime DEFAULT NULL,
PRIMARY KEY ( `id` )
) ENGINE = MyISAM AUTO_INCREMENT = 38 DEFAULT CHARSET = utf8;
"""
from test20260415 import create_app
app = create_app()
if __name__ == '__main__':
app.run(debug=True)
没了