python+mysql+bootstrap条件搜索分页

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>

9login.py

复制代码
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

10user.py

复制代码
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,更安全

12cache.py

复制代码
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)

13db.py

复制代码
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;
"""

14app.py

复制代码
from test20260415 import create_app
app = create_app()

if __name__ == '__main__':
    app.run(debug=True)

没了

相关推荐
AC赳赳老秦2 小时前
OpenClaw二次开发实战:编写专属办公自动化技能,适配个性化需求
linux·javascript·人工智能·python·django·测试用例·openclaw
Devin~Y2 小时前
大厂Java面试实录:Spring Boot/Cloud、Kafka、Redis、K8s 与 Spring AI(RAG/Agent)三轮连环问
java·spring boot·redis·mysql·spring cloud·kafka·kubernetes
Ulyanov3 小时前
《PySide6 GUI开发指南:QML核心与实践》 第二篇:QML语法精要——构建声明式UI的基础
java·开发语言·javascript·python·ui·gui·雷达电子对抗系统仿真
刀法如飞3 小时前
一款Python语言Django框架DDD脚手架,助你快速搭建项目
python·ddd·脚手架
刀法如飞3 小时前
一款Python语言Django框架DDD脚手架,适合中大型项目
后端·python·领域驱动设计
chenxu98b4 小时前
MySQL如何执行.sql 文件:详细教学指南
数据库·mysql
MediaTea4 小时前
Scikit-learn:数据集
人工智能·python·机器学习·scikit-learn
梦想的颜色4 小时前
mongoTemplate + Java 增删改查基础介绍
数据结构·数据库·mysql
木叶子---5 小时前
Spring 枚举转换器冲突问题分析与解决
java·python·spring