利用Flask来实现留言板的基本操作

article/2025/8/24 14:52:09

留言板开发

一、相关技术介绍

本项目基于现代Web开发技术栈构建,采用Python 3.12作为后端开发语言,搭配轻量级Flask 2.3.2框架实现核心功能。数据库层使用Flask-SQLAlchemy ORM框架操作MySQL 8.0数据库,通过对象关系映射简化数据操作。用户认证模块整合Flask-Login实现安全的会话管理,结合Flask-WTF处理表单验证,确保数据输入的合法性。前端界面采用Bootstrap 5框架构建响应式布局,通过Jinja2模板引擎实现动态内容渲染,整体架构遵循MVT(Model-View-Template)设计模式,实现业务逻辑、数据管理和表现层的清晰分离。详细如下

Python 3.12:作为后端编程语言

Flask 2.3.2:轻量级Web框架

Flask-SQLAlchemy:ORM框架,用于数据库操作

MySQL 8.0:关系型数据库存储数据

Flask-Login:用户认证管理

Flask-WTF:表单处理和验证

Bootstrap 5:前端UI框架

Jinja2:模板引擎

MVT设计模式:Model-View-Template架构

二、项目整体框架

1、代码目录结构

pythonProject/

├── venv/                   # PyCharm自动创建的虚拟环境

├── app/                    # 应用主目录

│   ├── __init__.py         # 应用初始化文件

│   ├── auth/               # 认证蓝图

│   │   ├── __init__.py

│   │   ├── forms.py

│   │   └── views.py

│   ├── main/               # 主蓝图

│   │   ├── __init__.py

│   │   ├── forms.py

│   │   └── views.py

│   ├── models.py           # 数据模型

│   └── templates/          # 模板文件

│       ├── auth/

│       │   ├── login.html

│       │   └── register.html

│        ── main/

│          ├── base.html

│          ├── create_message.html

│          ├── edit_message.html

│          ├── message_detail.html

│          └── messages.html

├── config.py               # 配置文件

├── requirements.txt        # 依赖文件

├── run.py                  # 启动文件

└── migrations/             # 数据库迁移文件夹(后续生成)

2、项目配置文件(config.py

import os

from dotenv import load_dotenv

basedir = os.path.abspath(os.path.dirname(__file__))

load_dotenv(os.path.join(basedir, '.env'))

class Config:

    SECRET_KEY = os.environ.get('SECRET_KEY') or 'dev-key-123'

    SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \

        'mysql+pymysql://username:password@localhost/flask_data'

    SQLALCHEMY_TRACK_MODIFICATIONS = False

    MESSAGES_PER_PAGE = 5

3、项目扩展包文件(requirements.txt)

Flask==2.3.2

Flask-SQLAlchemy==3.0.3

Flask-Login==0.6.2

Flask-WTF==1.1.1

Flask-Migrate==4.0.4

PyMySQL==1.0.3

python-dotenv==1.0.0

email-validator==2.0.0

三、数据库设计

1、对于数据库上的操作

检查MySQL服务状态

sudo systemctl status mysql

如果未运行,启动它

sudo systemctl start mysql

2、在mysql中创建数据库flask_data

CREATE DATABASE flask_data CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

3、数据库连接

CREATE DATABASE flask_data CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

GRANT ALL PRIVILEGES ON flask_data.* TO 'root'@'%' IDENTIFIED BY '12345678';

FLUSH PRIVILEGES;

EXIT;

  在pycharm终端执行

flask db init

flask db migrate -m "initial migration"

flask db upgrade

4、数据库表设计

App/models.py 核心代码

from datetime import datetime

from flask_login import UserMixin

from werkzeug.security import generate_password_hash, check_password_hash

from app import db, login

class User(UserMixin, db.Model):

    """用户表"""

    __tablename__ = 'users'

    

    id = db.Column(db.Integer, primary_key=True)

    username = db.Column(db.String(64), unique=True, nullable=False)

    email = db.Column(db.String(120), unique=True, nullable=False)

    password_hash = db.Column(db.String(255))  # 加密后的密码

    messages = db.relationship('Message', backref='author', lazy='dynamic')

    

    def set_password(self, password):

        self.password_hash = generate_password_hash(password)

    

    def check_password(self, password):

        return check_password_hash(self.password_hash, password)

class Message(db.Model):

    __tablename__ = 'messages'

    

    id = db.Column(db.Integer, primary_key=True)

    title = db.Column(db.String(100), nullable=False)

    content = db.Column(db.Text, nullable=False)

    user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)

    created_at = db.Column(db.DateTime, default=datetime.utcnow)

    updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)

三线表描述
  1. users表结构

字段名

类型

说明

id

INT

主键,自增

username

VARCHAR(64)

用户名,唯一

email

VARCHAR(120)

邮箱,唯一

password_hash

VARCHAR(255)

加密后的密码

created_at

DATETIME

创建时间

  1. messages表结构

字段名

类型

说明

id

INT

主键,自增

title

VARCHAR(100)

留言标题

content

TEXT

留言内容

user_id

INT

外键,关联users.id

created_at

DATETIME

创建时间

updated_at

DATETIME

最后更新时间

四、留言板模块设计

1、登陆模块

功能组成:登录模块提供完整的用户认证流程,包含四大核心功能:首先,用户注册功能实现账户创建,采用密码加密存储确保安全性;其次,登录功能支持凭证验证和会话管理;第三,退出功能可安全终止用户会话;最后,"记住我"选项通过持久化Cookie提升用户体验。所有密码均通过加密算法处理后再存储,即使数据库泄露也不会暴露原始密码。
核心代码 (auth/views.py):

from flask import render_template, redirect, url_for, flash, request
from flask_login import login_user, logout_user, current_user
from app import db
from app.auth import bp
from app.auth.forms import LoginForm, RegistrationForm
from app.models import User

@bp.route('/login', methods=['GET', 'POST'])
def login():
    if current_user.is_authenticated:
        return redirect(url_for('main.index'))
    form = LoginForm()
    if form.validate_on_submit():
        user = User.query.filter_by(username=form.username.data).first()
        if user is None or not user.check_password(form.password.data):
            flash('无效的用户名或密码')
            return redirect(url_for('auth.login'))
        login_user(user, remember=form.remember_me.data)
        next_page = request.args.get('next')
        return redirect(next_page) if next_page else redirect(url_for('main.index'))
    return render_template('auth/login.html', title='登录', form=form)

@bp.route('/logout')
def logout():
    print(f"退出前用户: {current_user}")  # 调试信息
    logout_user()
    print(f"退出后用户: {current_user}")  # 调试信息
    return redirect(url_for('main.index'))

@bp.route('/register', methods=['GET', 'POST'])
def register():
    if current_user.is_authenticated:
        return redirect(url_for('main.index'))
    form = RegistrationForm()
    if form.validate_on_submit():
        user = User(username=form.username.data, email=form.email.data)
        user.set_password(form.password.data)
        db.session.add(user)
        db.session.commit()
        flash('恭喜,注册成功!')
        return redirect(url_for('auth.login'))
    return render_template('auth/register.html', title='注册', form=form)

2、留言板模块

功能组成:留言板模块构成应用的核心功能体系:分页显示的留言列表提供内容概览;留言详情页展示完整内容和元信息;新增留言功能支持富文本内容创建;编辑功能允许作者修改已有内容;删除功能实现内容移除。每个功能点都进行了严格的权限控制,确保用户只能管理自己的内容,同时所有数据库操作都通过ORM进行,避免SQL注入风险

核心代码 (main/views.py):

@bp.route('/')

def index():

    page = request.args.get('page', 1, type=int)

    messages = Message.query.order_by(Message.created_at.desc()).paginate(

        page=page, per_page=current_app.config['MESSAGES_PER_PAGE'])

    return render_template('main/messages.html', messages=messages)

@bp.route('/create_message', methods=['GET', 'POST'])

@login_required

def create_message():

    form = MessageForm()

    if form.validate_on_submit():

        message = Message(

            title=form.title.data,

            content=form.content.data,

            author=current_user

        )

        db.session.add(message)

        db.session.commit()

        flash('留言已发布!')

        return redirect(url_for('main.index'))

    return render_template('main/create_message.html', form=form)

@bp.route('/message/<int:id>')

def message_detail(id):

    message = Message.query.get_or_404(id)

    return render_template('main/message_detail.html', message=message)

@bp.route('/edit_message/<int:id>', methods=['GET', 'POST'])

@login_required

def edit_message(id):

    message = Message.query.get_or_404(id)

    if message.author != current_user:

        abort(403)

    

    form = MessageForm()

    if form.validate_on_submit():

        message.title = form.title.data

        message.content = form.content.data

        db.session.commit()

        flash('留言已更新!')

        return redirect(url_for('main.message_detail', id=id))

    

    elif request.method == 'GET':

        form.title.data = message.title

        form.content.data = message.content

    

    return render_template('main/edit_message.html', form=form)

@bp.route('/delete_message/<int:id>', methods=['POST'])

@login_required

def delete_message(id):

    message = Message.query.get_or_404(id)

    if message.author != current_user:

        abort(403)

    

    db.session.delete(message)

    db.session.commit()

    flash('留言已删除!')

    return redirect(url_for('main.index'))

五、页面设计

1、登陆模块

以下我分别用dbb、hahaha、dbbb三个号来讲述

登录页面 (login.html) 特点:

登录页面采用Bootstrap 5框架实现响应式布局,能够完美适配从手机到桌面电脑的各种设备屏幕尺寸。表单设计实现了双重验证机制,既包含前端JavaScript实时验证,又通过Flask-WTF进行服务器端校验,确保数据安全性。错误提示采用醒目的红色边框和文字说明,直观展示验证失败的具体原因,帮助用户快速定位问题。"记住我"功能通过设置持久化Cookie实现,为用户提供便捷的自动登录体验。页面底部设有明显的注册入口链接,引导未注册用户快速跳转到注册页面,形成完整的用户引导流程。

代码如下

{% extends "main/base.html" %}
{% block title %}登录{% endblock %}
{% block content %}
<div class="row justify-content-center">
    <div class="col-md-6">
        <div class="card">
            <div class="card-body">
                <h2 class="card-title text-center mb-4">登录</h2>
                <form method="POST" action="{{ url_for('auth.login') }}">
                    {{ form.hidden_tag() }}
                    <div class="mb-3">
                        {{ form.username.label(class="form-label") }}
                        {{ form.username(class="form-control") }}
                        {% if form.username.errors %}
                            <div class="invalid-feedback d-block">
                                {% for error in form.username.errors %}
                                    {{ error }}
                                {% endfor %}
                            </div>
                        {% endif %}
                    </div>
                    
                    <div class="mb-3">
                        {{ form.password.label(class="form-label") }}
                        {{ form.password(class="form-control") }}
                        {% if form.password.errors %}
                            <div class="invalid-feedback d-block">
                                {% for error in form.password.errors %}
                                    {{ error }}
                                {% endfor %}
                            </div>
                        {% endif %}
                    </div>
                    <div class="mb-3 form-check">
                        {{ form.remember_me(class="form-check-input") }}
                        {{ form.remember_me.label(class="form-check-label") }}
                    </div>
                    <div class="d-grid">
                        {{ form.submit(class="btn btn-primary") }}
                    </div>
                </form>
                <div class="mt-3 text-center">
                    <p>新用户?<a href="{{ url_for('auth.register') }}">点击注册</a></p>
                </div>
            </div>
        </div>
    </div>
</div>
{% endblock %}

注册页面 (register.html) 特点:

注册页面实现了全面的表单验证体系,包括用户名唯一性检查(实时查询数据库)、标准邮箱格式验证(使用email-validator库)以及密码强度要求(最少8位字符)。密码确认字段通过WTForms的EqualTo验证器确保两次输入一致,防止输入错误。所有验证结果都会通过动态错误提示实时反馈,在输入框下方即时显示具体错误信息,大幅提升填写效率。页面保留了良好的用户引导设计,底部设有登录入口链接,方便已有账号的用户快速切换到登录界面,形成注册-登录的闭环体验。

代码如下:{% extends "main/base.html" %}

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

{% block content %}
<div class="row justify-content-center">
    <div class="col-md-6">
        <div class="card">
            <div class="card-body">
                <h2 class="card-title text-center mb-4">注册</h2>
                
                <form method="POST" action="{{ url_for('auth.register') }}">
                    {{ form.hidden_tag() }}
                    
                    <div class="mb-3">
                        {{ form.username.label(class="form-label") }}
                        {{ form.username(class="form-control") }}
                        {% if form.username.errors %}
                            <div class="invalid-feedback d-block">
                                {% for error in form.username.errors %}
                                    {{ error }}
                                {% endfor %}
                            </div>
                        {% endif %}
                    </div>
                    
                    <div class="mb-3">
                        {{ form.email.label(class="form-label") }}
                        {{ form.email(class="form-control") }}
                        {% if form.email.errors %}
                            <div class="invalid-feedback d-block">
                                {% for error in form.email.errors %}
                                    {{ error }}
                                {% endfor %}
                            </div>
                        {% endif %}
                    </div>
                    
                    <div class="mb-3">
                        {{ form.password.label(class="form-label") }}
                        {{ form.password(class="form-control") }}
                        {% if form.password.errors %}
                            <div class="invalid-feedback d-block">
                                {% for error in form.password.errors %}
                                    {{ error }}
                                {% endfor %}
                            </div>
                        {% endif %}
                    </div>
                    
                    <div class="mb-3">
                        {{ form.password2.label(class="form-label") }}
                        {{ form.password2(class="form-control") }}
                        {% if form.password2.errors %}
                            <div class="invalid-feedback d-block">
                                {% for error in form.password2.errors %}
                                    {{ error }}
                                {% endfor %}
                            </div>
                        {% endif %}
                    </div>
                    
                    <div class="d-grid">
                        {{ form.submit(class="btn btn-primary") }}
                    </div>
                </form>
                
                <div class="mt-3 text-center">
                    <p>已有账号?<a href="{{ url_for('auth.login') }}">点击登录</a></p>
                </div>
            </div>
        </div>
    </div>
</div>
{% endblock %}

2、留言板模块

基础模板 (base.html) 特点:

基础模板采用Bootstrap 5框架构建统一的UI风格,确保整个应用保持一致的视觉效果。响应式导航栏能自动适应不同设备屏幕尺寸,并根据用户登录状态动态显示对应的功能入口(如登录用户显示"发布留言"和"退出"按钮,未登录用户显示"登录"和"注册"按钮)。通过Flask的消息闪现系统,所有操作结果都会以醒目的提示框形式展示在页面顶部,增强用户交互体验。导航栏还实时显示当前登录用户名,让用户随时感知自己的登录状态。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}留言板{% endblock %}</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <style>
        body {
            padding-top: 20px;
            background-color: #f8f9fa;
        }
        .message-card {
            margin-bottom: 20px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
        .pagination {
            justify-content: center;
            margin-top: 20px;
        }
        /* 确保导航栏项目正确对齐 */
        .navbar-nav {
            align-items: center;
        }
    </style>
</head>
<body>
    <div class="container">
        <nav class="navbar navbar-expand-lg navbar-dark bg-dark mb-4">
            <div class="container-fluid">
                <a class="navbar-brand" href="{{ url_for('main.index') }}">留言板</a>
                <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarCollapse">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="collapse navbar-collapse" id="navbarCollapse">
                    <ul class="navbar-nav me-auto">
                        <li class="nav-item">
                            <a class="nav-link" href="{{ url_for('main.index') }}">首页</a>
                        </li>
                        {% if current_user.is_authenticated %}
                        <li class="nav-item">
                            <a class="nav-link" href="{{ url_for('main.create_message') }}">发布留言</a>
                        </li>
                        {% endif %}
                    </ul>
                    <ul class="navbar-nav">
                        {% if current_user.is_authenticated %}
                        <li class="nav-item">
                            <span class="navbar-text me-2">欢迎, {{ current_user.username }}</span>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link" href="{{ url_for('auth.logout') }}" id="logoutLink">退出</a>
                        </li>
                        {% else %}
                        <li class="nav-item">
                            <a class="nav-link" href="{{ url_for('auth.login') }}">登录</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link" href="{{ url_for('auth.register') }}">注册</a>
                        </li>
                        {% endif %}
                    </ul>
                </div>
            </div>
        </nav>

        {% with messages = get_flashed_messages(with_categories=true) %}
            {% if messages %}
                {% for category, message in messages %}
                    <div class="alert alert-{{ category }} alert-dismissible fade show">
                        {{ message }}
                        <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
                    </div>
                {% endfor %}
            {% endif %}
        {% endwith %}

        {% block content %}{% endblock %}
    </div>

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
    <script>
        // 确保退出链接正常工作
        document.getElementById('logoutLink')?.addEventListener('click', function() {
            // 可以在这里添加确认对话框
            return confirm('确定要退出吗?');
        });
    </script>
</body>
</html>

留言列表 (messages.html) 特点:

留言列表页面采用卡片式布局展示内容摘要,每张卡片包含留言标题、部分内容和关键元信息。分页控件位于页面底部,支持大量数据的分批加载和浏览。每条留言卡片都清晰标注作者用户名和发布时间,并采用条件渲染技术,仅对留言作者显示"编辑"和"删除"操作按钮。卡片标题链接到详情页,方便用户查看完整内容。整体布局简洁明了,信息层级分明。

代码如下:

{% extends "main/base.html" %}

{% block title %}留言板{% endblock %}

{% block content %}
<div class="row">
    <div class="col-md-8 mx-auto">
        <h1 class="mb-4">所有留言</h1>

        {% for message in messages.items %}
        <div class="card message-card mb-3">
            <div class="card-body">
                <h2 class="card-title">
                    <a href="{{ url_for('main.message_detail', id=message.id) }}" class="text-decoration-none">
                        {{ message.title }}
                    </a>
                </h2>
                <p class="card-text text-muted">
                    {{ message.content[:100] }}...  <!-- 显示前100个字符 -->
                </p>
                <div class="d-flex justify-content-between align-items-center">
                    <small class="text-muted">
                        作者: {{ message.author.username }} |
                        发布于: {{ message.created_at.strftime('%Y-%m-%d %H:%M') }}
                    </small>
                    {% if current_user == message.author %}
                    <div>
                        <a href="{{ url_for('main.edit_message', id=message.id) }}" class="btn btn-sm btn-outline-primary">编辑</a>
                    </div>
                    {% endif %}
                </div>
            </div>
        </div>
        {% else %}
        <div class="alert alert-info">暂无留言</div>
        {% endfor %}

        <!-- 分页导航 -->
        <nav aria-label="Message navigation">
            <ul class="pagination justify-content-center">
                {% if messages.has_prev %}
                <li class="page-item">
                    <a class="page-link" href="{{ url_for('main.index', page=messages.prev_num) }}">上一页</a>
                </li>
                {% endif %}

                <li class="page-item disabled">
                    <span class="page-link">第 {{ messages.page }} 页 / 共 {{ messages.pages }} 页</span>
                </li>

                {% if messages.has_next %}
                <li class="page-item">
                    <a class="page-link" href="{{ url_for('main.index', page=messages.next_num) }}">下一页</a>
                </li>
                {% endif %}
            </ul>
        </nav>

        <div class="text-center mt-3">
            <a href="{{ url_for('main.create_message') }}" class="btn btn-primary">发布新留言</a>
        </div>
    </div>
</div>
{% endblock %}

留言详情 (message_detail.html) 特点:

详情页完整展示留言的标题和内容,在显著位置标注作者信息和创建时间。如果留言被编辑过,还会额外显示最后更新时间,让读者了解内容的新鲜度。页面底部为留言作者提供了专属操作区,包含"编辑"和"删除"按钮,非作者用户则看不到这些功能入口。所有内容采用舒适的排版和合适的字体大小,确保最佳阅读体验。

代码如下:

{% extends "main/base.html" %}

{% block title %}{{ message.title }}{% endblock %}

{% block content %}
<div class="row">
    <div class="col-md-8 mx-auto">
        <div class="card message-card">
            <div class="card-body">
                <h2 class="card-title">{{ message.title }}</h2>
                <div class="card-text mb-4">
                    {{ message.content|safe }}
                </div>
                <div class="d-flex justify-content-between align-items-center">
                    <small class="text-muted">
                        作者: {{ message.author.username }} |
                        发布于: {{ message.created_at.strftime('%Y-%m-%d %H:%M') }} |
                        最后更新: {{ message.updated_at.strftime('%Y-%m-%d %H:%M') }}
                    </small>
                    {% if current_user == message.author %}
                    <div>
                        <a href="{{ url_for('main.edit_message', id=message.id) }}" class="btn btn-outline-primary">编辑</a>
                        <form action="{{ url_for('main.delete_message', id=message.id) }}" method="post" class="d-inline">
                            <button type="submit" class="btn btn-outline-danger" οnclick="return confirm('确定删除吗?')">删除</button>
                        </form>
                    </div>
                    {% endif %}
                </div>
            </div>
        </div>

        <div class="mt-3">
            <a href="{{ url_for('main.index') }}" class="btn btn-secondary">返回列表</a>
        </div>
    </div>
</div>
{% endblock %}

创建/编辑页面 (create_message.html/edit_message.html) 特点:

这两个页面共享相似的表单结构,都实现了严格的表单验证机制,包括必填项检查和内容长度限制。富文本编辑区域支持基本的格式设置,提升内容输入体验。在编辑模式下,表单会自动载入原有数据,方便用户修改。所有破坏性操作(如删除)都要求二次确认,通过JavaScript弹窗提示用户确认操作,有效防止误操作导致的数据丢失。页面布局保持简洁,突出核心的表单区域。

create_message.html:
{% extends "main/base.html" %}

{% block title %}发布留言{% endblock %}

{% block content %}
<div class="row">
    <div class="col-md-8 mx-auto">
        <h1 class="mb-4">发布留言</h1>

        <form method="POST" action="{{ url_for('main.create_message') }}">
            {{ form.hidden_tag() }}

            <div class="mb-3">
                {{ form.title.label(class="form-label") }}
                {{ form.title(class="form-control") }}
                {% if form.title.errors %}
                    <div class="invalid-feedback d-block">
                        {% for error in form.title.errors %}
                            {{ error }}
                        {% endfor %}
                    </div>
                {% endif %}
            </div>

            <div class="mb-3">
                {{ form.content.label(class="form-label") }}
                {{ form.content(class="form-control", rows=8) }}
                {% if form.content.errors %}
                    <div class="invalid-feedback d-block">
                        {% for error in form.content.errors %}
                            {{ error }}
                        {% endfor %}
                    </div>
                {% endif %}
            </div>

            <div class="d-grid">
                {{ form.submit(class="btn btn-primary") }}
            </div>
        </form>
    </div>
</div>
{% endblock %}
edit_message.html:

{% extends "main/base.html" %}

{% block title %}编辑留言{% endblock %}

{% block content %}
<div class="row">
    <div class="col-md-8 mx-auto">
        <h1 class="mb-4">编辑留言</h1>
        
        <form method="POST" action="{{ url_for('main.edit_message', id=message.id) }}">
            {{ form.hidden_tag() }}
            
            <div class="mb-3">
                {{ form.title.label(class="form-label") }}
                {{ form.title(class="form-control") }}
                {% if form.title.errors %}
                    <div class="invalid-feedback d-block">
                        {% for error in form.title.errors %}
                            {{ error }}
                        {% endfor %}
                    </div>
                {% endif %}
            </div>
            
            <div class="mb-3">
                {{ form.content.label(class="form-label") }}
                {{ form.content(class="form-control", rows=8) }}
                {% if form.content.errors %}
                    <div class="invalid-feedback d-block">
                        {% for error in form.content.errors %}
                            {{ error }}
                        {% endfor %}
                    </div>
                {% endif %}
            </div>
            
            <div class="d-grid gap-2 d-md-flex justify-content-md-end">
                <a href="{{ url_for('main.message_detail', id=message.id) }}" class="btn btn-secondary me-md-2">取消</a>
                {{ form.submit(class="btn btn-primary") }}
            </div>
        </form>
    </div>
</div>
{% endblock %}

第一次进入页面

使用dbbb这个号第三次进入页面就有之前两个的留言


用第三个dbbb号进入并发布新留言

编辑留言

成功删除留言


http://www.hkcw.cn/article/nFpDYAFqed.shtml

相关文章

vue3: tmap (腾讯地图)using typescript

项目结构&#xff1a; <!--* ___-_ _-___* _--^^^#####// \\#####^^^--_* _-^##########// ( ) \\##########^-_* -############// |\^^/| \\############-* _/############// (::) \############\_* …

【Linux】线程概念

&#x1f4dd;前言&#xff1a; 这篇文章我们来讲讲Linux——线程概念&#xff1a; 线程的基本概念线程的优缺点线程与进程 &#x1f3ac;个人简介&#xff1a;努力学习ing &#x1f4cb;个人专栏&#xff1a;Linux &#x1f380;CSDN主页 愚润求学 &#x1f304;其他专栏&am…

技术文档撰写指南:从结构到细节的全流程解析

在技术领域&#xff0c;一份优质的技术文档不仅是项目成果的载体&#xff0c;更是技术思想的可视化表达。本文结合《汽车导航系统电路设计及故障分析》课程设计说明书&#xff0c;拆解技术文档的核心要素&#xff0c;提供可复用的撰写范式&#xff0c;助力技术内容高效传播。 …

如何用AI设计海报,DeepSeek+即梦免费批量生成

大家好&#xff0c;这里是K姐。 一个帮助你把AI真正用起来的女子。 佳节将至&#xff0c;还在为节日海报而苦恼吗&#xff1f; 520刚过&#xff0c;端午节、六一儿童节、618就接踵而至&#xff0c;满街满屏的海报让人眼花缭乱。 做自媒体电商以及实体店的小伙伴现在已经一个…

全国一体化算力体系建设:破解算力困局,赋能数字经济新未来​

在数字经济蓬勃发展的当下&#xff0c;算力作为核心生产力&#xff0c;正面临着前所未有的挑战与机遇。从 GPT3.5 到 GPT4 的升级&#xff0c;算力需求呈现跳跃式增长&#xff0c;需要至少提高 3 到 5 倍的算力&#xff0c;国内算力供应出现断层&#xff0c;难以满足当前需求。…

window安装nginx

步骤1&#xff1a;下载Nginx for Windows​ 访问Nginx官网下载页面&#xff1a;https://nginx.org/en/download.html 在​​Stable version​​&#xff08;稳定版&#xff09;下找到Windows版本&#xff0c;点击下载.zip文件&#xff08;如 nginx-1.28.0.zip&#xff09; 步…

秋招Day11 - JVM - 垃圾回收

讲讲JVM的垃圾回收机制 垃圾回收是指JVM对内存中已经死亡的&#xff0c;不再使用的对象进行清除或回收。 常见的垃圾回收算法有标记-复制&#xff0c;标记-整理&#xff0c;标记-清除&#xff0c;分代收集算法等 一般的垃圾回收。过程是先使用可达性分析算法得出内存中哪些对…

Deepseek应用技巧-Dify安装和踩坑指南

前言&#xff1a;Dify的名号是非常大的&#xff0c;作为私有化AI部署中必不可少的一个组件&#xff0c;他的功能和COZE十分相似&#xff0c;可以进行工作流和智能体的搭建&#xff0c;有非常强大的功能&#xff0c;那本节就将来揭开Dify的神秘的面纱&#xff0c;首先看一下Dify…

[python] argparse怎么指定bool类型?

前述 最近在写脚本的时候想要实现一个if 操作&#xff0c;通过用户输入。确定要不要启用某个语句。 非常自然的就是使用python的argparse包&#xff0c;但是发现了一个陷阱&#xff0c;记录下。 陷阱 argparse.ArgumentParser() 可以指定输入类型&#xff0c;我可以设定为bo…

尚硅谷redis7 86 redis集群分片之3主3从集群搭建

86 redis集群分片之3主集群搭建 3主3从redis集群配置 找3台真实虚拟机,各自新建 mķdir -p /myredis/cluster 新建6个独立的redis实例服务 IP:192.168.111.175端口6381/端口6382 vim /myredis/cluster/redisCluster6381.conf bind 0.0.0.0 daemonize yes protected-mode no …

交集、差集、反选

1.交集&#xff1a;两个ROI相交的部分 dev_open_window (0, 0, 512, 512, black, WindowHandle) read_image (Image, clip) threshold (Image, Region, 0, 128) * 交集 intersection (Circle1, Circle2, RegionIntersection) 最终效果如下图所示&#xff1a; 2.差集&#xff1a…

Flutter GridView网格组件

目录 常用属性 GridView使用配置 GridView.count使用 GridView.extent使用 GridView.count Container 实现列表 GridView.extent Container 实现列表 GridView.builder使用 GridView网格布局在实际项目中用的也是非常多的&#xff0c;当我们想让可以滚动的元素使用矩阵…

The 2020 ICPC Asia Yinchuan Regional Programming Contest

A. Best Player 关于投影到坐标轴上&#xff0c;投影到x轴上&#xff0c;确实如果两个点的y值一样&#xff0c;会导致重影&#xff0c;但不能只看只看y轴的影响&#xff0c;还有要注意输出。 #include<bits/stdc.h> using namespace std; typedef long long ll; const l…

寄存器模型

8.layering sequence &#xff08;1&#xff09;概述 转化&#xff1a;高抽象级item&#xff0c;中间的sequence&#xff0c;低抽象级item。 &#xff08;2&#xff09;寄存器模型的示意图 &#xff08;3&#xff09;示例代码&#xff1a;bus packet sequence &#xff08;4&…

Python训练营打卡Day39

DAY 39 图像数据与显存 知识点回顾 1.图像数据的格式&#xff1a;灰度和彩色数据 2.模型的定义 3.显存占用的4种地方 a.模型参数梯度参数 b.优化器参数 c.数据批量所占显存 d.神经元输出中间状态 4.batchisize和训练的关系 作业&#xff1a;今日代码较少&#xff0c;理解内容…

AI预测3D新模型百十个定位预测+胆码预测+去和尾2025年5月29日第92弹

从今天开始&#xff0c;咱们还是暂时基于旧的模型进行预测&#xff0c;好了&#xff0c;废话不多说&#xff0c;按照老办法&#xff0c;重点8-9码定位&#xff0c;配合三胆下1或下2&#xff0c;杀1-2个和尾&#xff0c;再杀6-8个和值&#xff0c;可以做到100-300注左右。 (1)定…

【AI智能体】Coze 插件从使用到实战详解

目录 一、前言 二、Coze 插件介绍 2.1 什么是插件 2.1.1 插件主要功能 2.1.2 coze 插件常用类型 2.1.3 coze 插件优势 2.2 插件与工具 2.3 插件费用说明 2.4 使用限制 2.5 权限说明 2.6 coze添加插件入口 三、Coze插件创建与使用 3.1 前置准备 3.1.1 查看api列表 …

python实现一个示波器仿真,可以改参数同步效果

代码 import sys import numpy as np import matplotlib.pyplot as plt from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar from matplotlib.an…

【前端】使用grid布局封装断点式进度条

业务需求要求展示一个动态进度条&#xff0c;不同于第三方插件的进度条&#xff0c;这个ui设计的是断点式进度条&#xff0c;效果当然是美观大方&#xff0c;但是因为没有现成的第三方插件可以实现&#xff0c;这当然难不倒一个成熟的前端开发工程师。 根据ui设计可以看到进度条…

[ctfshow web入门] web78

信息收集 表面上没有任何过滤&#xff0c;自由发挥 if(isset($_GET[file])){$file $_GET[file];include($file); }else{highlight_file(__FILE__); }解题 这些解法都在web32解释过&#xff0c;不再详细解释&#xff0c;有需要点击超链接&#xff1a;[ctfshow web入门] web3…