Flask快速入门和问答项目源码

news2025/7/19 15:05:59

Flask基础入门
源码:

  • gitee:我爱白米饭/Flask问答项目 - 码云

目录

    • 1.安装环境
    • 2.【debug、host、port】
    • 3.【路由params和query】
    • 4.【模板】
    • 5.【静态文件】
    • 6.【数据库连接】
      • 6.1.安装模块
      • 6.2.创建数据库并测试连接
      • 6.3.创建数据表
      • 6.4.ORM
      • 6.5.ORM模型外键
      • 6.6.映射和迁移
    • 7.【问答平台项目】
      • 7.1.环境准备
        • 7.1.1 安装python环境
        • 7.1.2.创建文件
        • 7.1.3.绑定配置文件
      • 7.2.创建数据库
      • 7.3.创建蓝图
        • 7.3.1.模板导航栏
        • 7.3.2.用户注册和登录
          • 🌟注册模板
          • 🌟表单验证
          • 🌟登录模板
          • 🌟后端实现
        • 7.3.3.用户问答和搜索
          • 🌟问答和主页模板
          • 🌟问答和主页视图
          • 🌟问答详情和解答模板
          • 🌟问答详情和解答视图
          • 🌟搜索功能
          • 🌟总视图代码
    • 8.【总结】


正文内容如下:

1.安装环境

python - m venv .venv
pip install Flask

创建第一个实例

from flask import Flask

# __name__:代表当前app.py这个模块
# 1.以后出现bug,他可以帮助我们快速定位
# 2.对于寻找模板文件,有一个相对路径
# 使用Flak类创建一个app对象
app = Flask(__name__)

@app.route('/')
def hello_world():  # put application's code here
    return 'Hello World!'

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

2.【debug、host、port】

from flask import Flask
# 使用Flak类创建一个app对象
app = Flask(__name__)

@app.route('/')
def hello_world():  # put application's code here
    return 'Hello World!'

if __name__ == '__main__':
    app.run(debug=True,host='0.0.0.0',port=8000)  # 开启Debug模式、修改地址host、访问端口号port

3.【路由params和query】

from flask import Flask, request, render_template

app = Flask(__name__)

@app.route('/user/<username>')
def user(username):
    return f"用户名是{username}"


# 年月日参数,http://127.0.0.1:5000/date/2025/05/16
@app.route('/date/<int:year>/<int:month>/<int:day>')
def date(year, month, day):
    return f"今天日期是{year}{month}{day}日"


# /book/list?page=1&size=10
@app.route('/book/list')
def book_list():
    # arguments = {'page': 1, 'size': 10}
    # request.args:类字典类型
    page = request.args.get('page', 1, type=int)
    size = request.args.get('size', 10, type=int)
    return 'book_list/page=%s,size=%s' % (page, size)

4.【模板】

from flask import Flask, request, render_template

app = Flask(__name__)
app.debug = True  # 可选:显式设置 debug 模式

@app.route('/detail/')
def BookDetail():
    return render_template("book_detail.html", title='Flask入门', author='李华',context={'title': 'Flask高级', 'author': '王明'})

目录结构,需要创建templates

├─static
├─templates
│  └─book_detail.html
├─app.py

5.【静态文件】

目录结构

├─static
│  └─css
│  └─js
│  └─img
├─templates
│  └─static_img.html
├─app.py

app.py

from flask import Flask, request, render_template

app = Flask(__name__)
app.debug = True  # 可选:显式设置 debug 模式

@app.route('/img/')
def img():
    return render_template('static_img.html')

if __name__ == '__main__':
    app.run(debug=True)  # 开启Debug模式

static_img.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <img src="{{ url_for('static', filename='img/1.jpg') }}" alt="">
</body>
</html>

结果如下:

当然这是一个图片的案例,如果想引入cssjs等或者其他文件内容的话,将filename='img/1.jpg'的值替换一下就可以

其中,模板中也有过滤器的使用,可以自行网上查找,和django类似

6.【数据库连接】

6.1.安装模块

需要安装两个模块 flask_sqlalchemypymysql

pip install flask_sqlalchemy
pip install pymysql    

6.2.创建数据库并测试连接

创建数据库

 create database flask_test default charset="utf8";

测试连接 ---- app.py

from flask import Flask, request, render_template
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.debug = True  # 可选:显式设置 debug 模式

# 配置数据库
HOSTNAME = '127.0.0.1'
PORT = 3306
USERNAME = 'root'
PASSWORD = '123456'
DATABASE = 'flask_test'
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)

db = SQLAlchemy(app)
# 将数据库操作放在应用上下文中
with app.app_context():
    with db.engine.connect() as conn:
        print("连接成功")

# 创建视图
@app.route('/')
def hello_world():  # put application's code here
    return 'Hello World!'

if __name__ == '__main__':

    app.run(debug=True)  # 开启Debug模式

注意:不要上下文缺失with app.app_context():db.engine 需要应用上下文来读取配置(如数据库URI),但你的代码在应用启动前或上下文外调用了它

6.3.创建数据表

from flask import Flask, request, render_template
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.debug = True  # 可选:显式设置 debug 模式

# 配置数据库
HOSTNAME = '127.0.0.1'
PORT = 3306
USERNAME = 'root'
PASSWORD = '123456'
DATABASE = 'flask_test'
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME, PASSWORD,HOSTNAME, PORT, DATABASE)

db = SQLAlchemy(app)

# 创建数据表
class User(db.Model):
    __tablename__ = 'user'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    password = db.Column(db.String(120), unique=True, nullable=False)

with app.app_context():
    db.create_all()

6.4.ORM

数据

from flask import Flask, request, render_template
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.debug = True  # 可选:显式设置 debug 模式

# 配置数据库
HOSTNAME = '127.0.0.1'
PORT = 3306
USERNAME = 'root'
PASSWORD = '123456'
DATABASE = 'flask_test'
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME, PASSWORD,
                                                                                             HOSTNAME, PORT, DATABASE)

db = SQLAlchemy(app)

with app.app_context():
    db.create_all()

# 创建数据表
class User(db.Model):
    __tablename__ = 'user'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    password = db.Column(db.String(120), unique=True, nullable=False)


with app.app_context():
    db.create_all()


if __name__ == '__main__':
    # app.run(debug=True,host='0.0.0.0',port=8000)  # 开启Debug模式、修改地址host、访问端口号port
    app.run(debug=True)  # 开启Debug模式

@app.route('/user/add/')
def add_user():
    # 1.创建ORM对象
    user = User(username='admin', password='123456')
    # 2.添加到会话
    db.session.add(user)
    # 3.提交事务
    db.session.commit()
    return "添加成功"
@app.route('/user/delete/')
def delete_user():
    # 1.查询用户
    user = User.query.get(1)
    # 2.删除用户
    db.session.delete(user)
    # 3.提交事务
    db.session.commit()
    return "删除成功"
@app.route('/user/update/')
def update_user():
    # 1.查询用户
    user = User.query.get(1)
    # 2.更新用户信息
    user.password = '666666'
    # 3.提交事务
    db.session.commit()
    return "更新成功"
@app.route('/user/query/')
def query_user():
    # 1.查询所有用户,get单个查找
    user = User.query.get(1)
    print(user.id, user.username, user.password)
    #  filter过滤查找,可以批量查找
    users = User.query.filter().all()
    for u in users:
        print(u.id, u.username, u.password)
    return "查询成功"

6.5.ORM模型外键

  • 第一种方式back_populates

    class User(db.Model):
        __tablename__ = 'user'
        id = db.Column(db.Integer, primary_key=True, autoincrement=True)
        username = db.Column(db.String(80), unique=True, nullable=False)
        password = db.Column(db.String(120), unique=True, nullable=False)
        # 添加外键文章
        articles =db.relationship('Article', back_populates='author')
    
    class Article(db.Model):
        __tablename__ = 'article'
        id = db.Column(db.Integer, primary_key=True, autoincrement=True)
        title = db.Column(db.String(80), unique=True, nullable=False)
        content = db.Column(db.Text, unique=True, nullable=False)
    
        # 添加外键作者
        author_id = db.Column(db.Integer, db.ForeignKey('user.id'))
        # 添加关系属性 ,如果使用的是back_populates的话,那么在User类中也要添加articles属性
        author = db.relationship('User', back_populates='articles')
    
  • 第二种backref

class User(db.Model):
    __tablename__ = 'user'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    password = db.Column(db.String(120), unique=True, nullable=False)

class Article(db.Model):
    __tablename__ = 'article'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    title = db.Column(db.String(200), unique=True, nullable=False)
    content = db.Column(db.Text, unique=True, nullable=False)

    # 添加外键作者
    author_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    # 添加关系属性 ,如果使用的是back_populates的话,那么在User类中也要添加articles属性
    author = db.relationship('User', backref='articles')

文章的

@app.route('/article/add/')
def add_article():
    article = Article(title='Flask框架基础', content='教大家如何快速掌握Flask')
    article.author = User.query.get(1)
    # article2 = Article(title='Django框架基础',content='教大家如何快速掌握Django',author_id=1)
    article2 = Article(title='Django框架基础', content='教大家如何快速掌握Django', author_id=1)
    #  一次性添加多个数据使用add_all,传入的内容是一个列表
    db.session.add_all([article, article2])
    db.session.commit()
    return "添加成功"


@app.route('/article/query/')
def query_article():
    # 通过user表种的userid获取所有的文章
    user = User.query.get(1)
    for a in user.articles:
        print(a.id, a.title, a.content, a.author.username)
    #  通过article表获取查询文章
    articles = Article.query.filter().all()
    for a in articles:
        print(a.id, a.title, a.content, a.author.username)
    return "查询成功"

6.6.映射和迁移

安装模块flask-migrate

pip install flask-migrate 

之前使用的是

# 练习创建数据表使用
with app.app_context():
    db.create_all()

在开发过程中使用映射

from flask import Flask, request, render_template
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
app = Flask(__name__)
app.debug = True  # 可选:显式设置 debug 模式

# 配置数据库
HOSTNAME = '127.0.0.1'
PORT = 3306
USERNAME = 'root'
PASSWORD = '123456'
DATABASE = 'flask_test'
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME, PASSWORD,
                                                                                             HOSTNAME, PORT, DATABASE)

db = SQLAlchemy(app)
migrate = Migrate(app, db) // 创建就行

命令使用

  • 第一步,生成环境,类似git的init

    flask db init
    

    会生成一个migrations文件

    ├─migrations
    │  └─versions
    │  └─alembic.ini
    │  └─env.py
    │  └─README
    │  └─script.py.mako
    ├─static
    ├─templates
    
  • 第二步,识别ORM模型的改变,生成迁移脚本

    flask db migrate
    
  • 第三步,运行迁移脚本,同步到数据库中

    flask db upgrade
    

7.【问答平台项目】

7.1.环境准备

7.1.1 安装python环境

创建项目,其实就是一个文件夹的创建,进入文件夹后,需要配置下虚拟环境,和安装Flask模块

python - m venv .venv

进入虚拟环境

.venv\Scripts\activate

安装依赖库

pip install Flask
# 数据库连接
pip install flask_sqlalchemy
pip install pymysql   
# 数据库迁移和映射
pip install flask-migrate 
# 邮箱模块
pip install flask-mail
# 表单验证
pip install flask-wtf
# 邮箱验证
pip install email_validator
pip install cryptography 
7.1.2.创建文件
├─static        # 静态资源
├─templates     # 模板
├─app.py        # 根
├─config.py     # 配置文件
├─exts.py		# 数据库配置信息
├─models.py		# 数据库表配置
├─blueprints    # 视图函数 & 后端请求
│  └─auth.py    # 视图函数
│  └─qa.py		# 视图函数
│  └─forms.py   # 表单验证模块
├─decorators.py # 拓展:装饰器,拦截功能

在这里插入图片描述

7.1.3.绑定配置文件
  • config.py配置

    # 配置数据库
    HOSTNAME = '127.0.0.1'
    PORT = 3306
    USERNAME = 'root'
    PASSWORD = '123456'
    DATABASE = 'flask_test'
    DB_URI = 'mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)
    SQLALCHEMY_DATABASE_URI = DB_URI
    
    # 发送邮箱配置
    # 授权码:ydevxpkfezjydddd ,授权码不是邮箱密码
    MAIL_SERVER = 'smtp.qq.com'
    MAIL_PORT = 587
    MAIL_USE_TLS = True
    MAIL_USERNAME = '你的邮箱'
    MAIL_PASSWORD = '授权码'
    MAIL_DEFAULT_SENDER = MAIL_USERNAME
    

    使用qq邮箱作为服务器,打开设置
    在这里插入图片描述

找到账号下的POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务,开启服务,并获取授权码
在这里插入图片描述
在这里插入图片描述

  • exts.py

    # 这个文件存在的意义就是为了解决循环引用的问题
    from flask_sqlalchemy import SQLAlchemy
    # 导入邮箱实例模块
    from flask_mail import Mail
    # 创建实例
    db = SQLAlchemy()
    mail = Mail()
    
  • app.py

    将配置的信息绑定app.py

    # 导入 flask模块
    from flask import Flask
    # 导入 配置文件
    import config
    # 导入 数据库实例和发送邮箱实例
    from exts import db, mail
    # 导入 视图函数,并使用别名
    from blueprints.auth import bp as auth_bp
    from blueprints.qa import bp as qa_bp
    # 导入 迁移和映射数据模块
    from flask_migrate import Migrate
    
    # 实例Flask对象
    app = Flask(__name__)
    app.debug = True  # 可选:显式设置 debug 模式,在开发过程中开启即可,上线时删掉这行或者设置为False
    
    # 绑定config配置文件
    app.config.from_object(config)
    
    # 初始化数据库db且绑定app
    db.init_app(app)
    # 初始化邮箱mail且绑定app
    mail.init_app(app)
    
    # 注册蓝图(相当于django中的视图)
    app.register_blueprint(auth_bp)
    app.register_blueprint(qa_bp)
    
    # 初始化数据库迁移(映射数据库)
    migrate = Migrate(app, db)
    
    if __name__ == '__main__':
        app.run()  
    

7.2.创建数据库

使用命令行创建数据库,打开cmd

pymysql -u用户名 -p密码

创建utf8编码的数据库

create database flask_test default charset="utf8";

进入models.py创建数据库表,没有这个文件的话创建一下

# 导入已经实例化的对象
from exts import db
# 导入python内置的时间模块
from datetime import datetime


class UserModel(db.Model):
    """用户表"""
    __tablename__ = 'user'
    # id:主键primary_key=True,自增:autoincrement=True
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    username = db.Column(db.String(20), nullable=False)
    password = db.Column(db.String(200), nullable=False)
    email = db.Column(db.String(100), nullable=False, unique=True)
    join_time = db.Column(db.DateTime, default=datetime.now())


class EmailCaptchaModel(db.Model):
    """邮箱验证码存储表"""
    __tablename__ = 'email_captcha'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    email = db.Column(db.String(100), nullable=False)
    captcha = db.Column(db.String(100), nullable=False)


class QuestionModel(db.Model):
    """问答表"""
    __tablename__ = 'question'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    title = db.Column(db.String(100), nullable=False)
    content = db.Column(db.Text, nullable=False)
    create_time = db.Column(db.DateTime, default=datetime.now())
    author_id = db.Column(db.Integer, db.ForeignKey('user.id'))  # 外键
    # 可以通过questions取到UserModel的username,如:question.author.username 
    author = db.relationship('UserModel', backref='questions')  # 反向引用


class AnswerModel(db.Model):
    """评论/回答表"""
    __tablename__ = 'answer'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    content = db.Column(db.Text, nullable=False)
    create_time = db.Column(db.DateTime, default=datetime.now())
    question_id = db.Column(db.Integer, db.ForeignKey('question.id'))
    author_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    question = db.relationship('QuestionModel', backref=db.backref('answers', order_by=create_time.desc()))
    author = db.relationship('UserModel', backref=db.backref('answers'))

在创建数据表的时候需要注意下外键的关系,外键的方向引用的用法,以及关联的数据表,当然,创建数据表还需要执行以下命令:

  • 第二步,创建并初始化文件【执行一次即可】
flask db init

会在根目录下生成一个migrations的迁移文件

├─migrations
│  └─versions
│  └─alembic.ini
│  └─env.py
│  └─README
│  └─script.py.mako
  • 第二步,识别ORM模型的改变,生成迁移脚本
flask db migrate
  • 第三步,运行迁移脚本,同步到数据库中
flask db upgrade

注意:如果后续需要修改models.py中的内容,只需执行第二步第三步即可,在Flask框架中初始化文件只执行一次

7.3.创建蓝图

蓝图其实就是一个python程序包,命名为:blueprints,在蓝图中创建两个视图文件(auth.py、qa.py)和一个表单验证文件(forms.py),目录结构为:

├─blueprints     # 视图函数 & 后端请求
│  └─__init__.py 
│  └─auth.py     # 视图函数
│  └─qa.py		 # 视图函数
│  └─forms.py    # 表单验证模块
7.3.1.模板导航栏

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-3.4.1-dist/css/bootstrap.min.css') }}">
    <script type="text/javascript" src="{{ url_for('static', filename='/jquery/jquery-3.7.1.min.js') }}"></script>
    <script type="text/javascript"
            src="{{ url_for('static', filename='/bootstrap-3.4.1-dist/js/bootstrap.min.js') }}"></script>

    <style>
        body {
            padding-top: 20px;
            padding-bottom: 20px;
        }

        {% block css %}{% endblock %}
    </style>
</head>
<body>
<div class="container">
    <nav class="navbar navbar-default">
        <div class="container-fluid">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar"
                        aria-expanded="false" aria-controls="navbar">
                    <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="/">Flask问答</a>
            </div>
            <div id="navbar" class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li class="active"><a href="/">首页</a></li>
                    <li><a href="{{ url_for('qa.public') }}">发布问答</a></li>
                </ul>
                <form class="navbar-form navbar-left" method="GET" action="{{ url_for('qa.search') }}">
                    <div class="form-group">
                        <input type="search" class="form-control" name="search" placeholder="Search">
                    </div>
                    <button type="submit" class="btn btn-default">搜索</button>
                </form>
                <ul class="nav navbar-nav navbar-right">
                    {% if user %}
                        <li><a href="{{ url_for('auth.logout') }}">退出登录</a></li>
                    {% else %}
                        <li><a href="{{ url_for('auth.login') }}">登录</a></li>
                        <li><a href="{{ url_for('auth.register') }}">注册</a></li>
                    {% endif %}
                </ul>
            </div><!--/.nav-collapse -->
        </div><!--/.container-fluid -->
    </nav>
    {% block content %}
    {% endblock %}
</div>
</body>
{% block js %}{% endblock %}
</html>
7.3.2.用户注册和登录

auth.py–用户注册,注册视图中有一个邮箱验证码的功能,在写用户注册的时候可以使用一个视图来验证邮箱验证码发送是否成功:

在发送之前还是需要环境准备中准备下邮箱服务器的配置,在config

from flask import Blueprint, render_template, request, redirect, url_for, session, jsonify
from exts import mail
from flask_mail import Message

bp = Blueprint("auth", __name__, url_prefix="/auth")

@bp.route("/mail/test/")
def send_mail():
    message = Message(subject="Hello", sender="服务器邮箱", recipients=["目的地邮箱"],
                      body="This is a test email.")
    mail.send(message)
    return "Mail sent successfully!"

在实现注册功能的时候,需要准备下前端视图模板

🌟注册模板

register.html

{% extends "base.html" %}
{% block title %}注册{% endblock %}
{% block css %}
    .form-signin {
    max-width: 400px;
    padding: 15px;
    margin: 0 auto;
    }
{% endblock %}

{% block content %}
    <div class="bs-example" data-example-id="panel-with-list-group">
        <!-- Default panel contents -->
        <div class="panel-heading"><h3 style="text-align: center">注册</h3></div>
        <div class="panel-body">
            <form class="form-signin" method="post">
                <div class="form-group">
                    <label for="email">邮箱:</label>
                    <input type="email" class="form-control" name="email" id="email" placeholder="Email">
                </div>
                <div class="form-group">
                    <label for="captcha">验证码:</label>
                    <div class="input-group">
                        <input type="text" id="captcha" name="captcha" class="form-control" placeholder="captcha">
                        <span class="input-group-btn">
            <button class="btn btn-primary" id="get_captcha" type="button">获取验证码</button>
          </span>
                    </div>
                </div>
                <div class="form-group">
                    <label for="username">用户名:</label>
                    <input type="text" name="username" class="form-control" id="username" placeholder="username">
                </div>
                <div class="form-group">
                    <label for="password">密码:</label>
                    <input type="password" name="password" class="form-control" id="password" placeholder="password">
                </div>
                <div class="form-group">
                    <label for="password_confirm">确认密码:</label>
                    <input type="password" name="password_confirm" class="form-control" id="password_confirm"
                           placeholder="password_confirm">
                </div>
                <button type="submit" class="btn btn-primary">Submit</button>
            </form>
        </div>
    </div>


{% endblock %}
{% block js %}
    <script type="text/javascript">

        $(function () {
            bindEmailCaptchaClick()
        })

        function bindEmailCaptchaClick() {
            $("#get_captcha").click(function (event) {
                var $this = $(this)
                // 阻止表单提交
                event.preventDefault()
                const email = $("input[name='email']").val()
                $.ajax({
                    url: "/auth/captcha/email?email=" + email, // 请求地址,
                    method: "POST",
                    success: function (result) {
                        var code = result.code
                        if (code === 200) {
                            var countdown = 60
                            // 开始倒计时之前,就取消按钮的点击事件
                            $this.off("click")
                            var timer = setInterval(function () {
                                $this.text(countdown + "s后重试")
                                countdown--
                                // 倒计时结束的时候执行
                                if (countdown === 0) {
                                    clearInterval(timer)
                                    $this.text("获取验证码")
                                    bindEmailCaptchaClick()
                                }
                            }, 1000)
                        }
                    },
                    fail: function (error) {
                        console.log(error)
                    }
                })
            })
        }
    </script>
{% endblock %}
🌟表单验证

forms.py

import wtforms
from wtforms.validators import Email, Length, EqualTo,InputRequired
from models import UserModel, EmailCaptchaModel
from exts import db


class RegisterForm(wtforms.Form):
    email = wtforms.StringField(validators=[Email(message='邮箱格式错误')])
    captcha = wtforms.StringField(validators=[Length(min=4, max=4, message='验证码格式错误')])
    username = wtforms.StringField(validators=[Length(min=3, max=20, message='用户名格式错误')])
    password = wtforms.StringField(validators=[Length(min=6, max=20, message='密码格式错误')])
    password_confirm = wtforms.StringField(validators=[EqualTo('password', message='两次输入的密码不一致')])

    # 自定义验证:
    # 邮箱是否已经被注册
    def validate_email(self, field):
        email = field.data
        user = UserModel.query.filter_by(email=email).first()
        if user:
            raise wtforms.ValidationError(message='邮箱已经被注册')

    # 验证码是否正确
    def validate_captcha(self, field):
        captcha = field.data
        email = self.email.data
        captcha_model = EmailCaptchaModel.query.filter_by(email=email, captcha=captcha).first()
        if not captcha_model:
            raise wtforms.ValidationError(message='邮箱验证码错误')

class LoginForm(wtforms.Form):
    email = wtforms.StringField(validators=[Email(message='邮箱格式错误')])
    password = wtforms.StringField(validators=[Length(min=6, max=20, message='密码格式错误')])

🌟登录模板

用户登录模板login.html

{% extends "base.html" %}
{% block title %}注册{% endblock %}
{% block css %}
    .form-signin {
    max-width: 400px;
    padding: 15px;
    margin: 0 auto;
    }
{% endblock %}

{% block content %}


    <div class="bs-example" data-example-id="panel-with-list-group">
        <!-- Default panel contents -->
        <div class="panel-heading"><h3 style="text-align: center">登录</h3></div>
        <div class="panel-body">
            <form class="form-signin" method="post">
                <div class="form-group">
                    <label for="email">邮箱:</label>
                    <input type="email" class="form-control" name="email" id="email" placeholder="Email">
                </div>
                <div class="form-group">
                    <label for="password">密码:</label>
                    <input type="password" name="password" class="form-control" id="password"
                           placeholder="password">
                </div>
                <div class="checkbox">
                    <label>
                        <input type="checkbox"> Check me out
                    </label>
                </div>
                <button type="submit" class="btn btn-primary">Submit</button>
            </form>
        </div>
    </div>
{% endblock %}
🌟后端实现

实现用户登录/注册功能,auth.py

from flask import Blueprint, render_template, request, redirect, url_for, flash, session, jsonify
# 导入数据和邮箱实例对象
from exts import mail, db
# 导入邮箱信息发送模块
from flask_mail import Message
# python内置库
import string
import random
# 导入两个数据表
from models import EmailCaptchaModel, UserModel
# 表单验证模块
from .forms import RegisterForm, LoginForm
# 写入数据库中的密码加密
from werkzeug.security import generate_password_hash, check_password_hash

bp = Blueprint("auth", __name__, url_prefix="/auth")

# 只接受两种请求,分别是GET和POST
@bp.route("/login", methods=["GET", "POST"])
def login():
    if request.method == "GET":
        # 如果是GET请求,则是返回模板
        return render_template("login.html")
    else:
        form = LoginForm(request.form)
        if form.validate():
            # 获取到表单填写的内容,注意前端的input的name属性是和这里的form.name.data,中的name是一一对应的
            email = form.email.data
            password = form.password.data
            user = UserModel.query.filter_by(email=email).first()
            if not user:
                print("用户不存在")
                # 重定向路由,实现连接跳转
                return redirect(url_for("auth.login"))  
            if check_password_hash(user.password, password):
                session['user_id'] = user.id
                return redirect('/')
                # return "登录成功"
            else:
                print("密码错误")
                return redirect(url_for("auth.login"))
        else:
            print(form.errors)
            return redirect(url_for("auth.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
            # 将数据保存到数据库中
            user = UserModel(username=username, password=generate_password_hash(password), email=email)
            db.session.add(user)
            db.session.commit()
            return redirect(url_for("auth.login"))
        else:
            print(form.errors)
            return redirect(url_for("auth.register"))


# http://127.0.0.1:5000/auth/captcha/email?email=2097969685@qq.com
@bp.route("/captcha/email", methods=["POST"])
def get_captcha():
    email = request.args.get("email")
    source = random.sample(string.digits * 4, 4)
    captcha = "".join(source)
    message = Message(subject="注册验证码", sender="2949666522@qq.com", recipients=[email],
                      body=f"您的邮箱验证码为:{captcha},有效期1分钟。")
    mail.send(message)
    email_captcha = EmailCaptchaModel(email=email, captcha=captcha)
    db.session.add(email_captcha)
    db.session.commit()
    return jsonify({"code": 200, "message": "", "data": ""})


@bp.route("/mail/test/")
def send_mail():
    message = Message(subject="Hello", sender="2949666522@qq.com", recipients=["2097969685@qq.com"],
                      body="This is a test email.")
    mail.send(message)
    return "Mail sent successfully!"


@bp.route("/logout")
def logout():
    session.clear()
    return redirect(url_for('auth.login'))

app.py中还需要添加

from models import UserModel

@app.before_request
def my_before_request():
    user_id = session.get('user_id')
    if user_id:
        user = UserModel.query.get(user_id)
        setattr(g, 'user', user)
    else:
        setattr(g, 'user', None)


@app.context_processor
def my_context_processor():
    return {'user': g.user}

效果:

在这里插入图片描述

在这里插入图片描述

7.3.3.用户问答和搜索

在写问答模块和搜索功能的时候,流程是:

问答模板----> 问答视图 ----> 权限设置(用户登录后才能访问问答页面) ----> 问答详情模板 ----> 问答详情视图 ----> 解答/评论模板 ----> 解答/评论视图 ----> 搜索功能实现

🌟问答和主页模板

index.html

{% extends 'base.html' %}

{% block title %}首页{% endblock %}
{% block content %}
    <div class="bs-example" data-example-id="panel-with-list-group">
        <!-- Default panel contents -->
        <div class="media-body">
            <div class="panel-heading"><h3 style="text-align: center">问答列表</h3></div>
            <ul class="list-group">
                {% for question in questions %}
                    <li class="list-group-item">
                        <a href="{{ url_for('qa.detail', question_id=question.id) }}">{{ question.title }}</a>
                        <div class="media-body">
                            <p class="media-heading">{{ question.content|truncate(length=100) }}</p>
                        </div>
                        <p class=" text-right"><span>{{ question.author.username }}  &nbsp&nbsp&nbsp  </span> {{ question.create_time }}</p>
                    </li>
                {% endfor %}
            </ul>
        </div>

    </div>
{% endblock %}

public.html

{% extends  'base.html' %}
{% block title %}发布问答{% endblock %}
{% block content %}
    <div class="panel panel-warning">
        <div class="panel-heading">
            <h3 class="text-center">发布问答</h3>
        </div>
        <div class="panel-body">
            <form class="form-signin" method="post">
                <div class="form-group">
                    <label for="title">标题:</label>
                    <input type="text" class="form-control" name="title" id="title" placeholder="请输入标题">
                </div>
                <div class="form-group">
                    <label for="content">内容:</label>
                    <textarea name="content" rows="14" cols="60" class="form-control" id="content" placeholder="请输入内容"></textarea>
                </div>
                <div class="text-right">
                    <button type="submit" class="btn btn-primary ">发布</button>
                </div>
            </form>
        </div>
    </div>
{% endblock %}

🌟问答和主页视图

这里有一个权限控制需要注意下,自定义权限,装饰器@login_required

存放在decorators.py

from functools import wraps
from flask import g, redirect, url_for


def login_required(func):
    # 保留func的信息
    @wraps(func)
    # *args, **kwargs是装饰器的参数,*args表示位置参数,**kwargs表示关键字参数
    def inner(*args, **kwargs):
        # 在func执行之前,先判断用户是否登录
        if g.user:
            return func(*args, **kwargs)
        else:
            return redirect(url_for('auth.login'))

    return inner

forms.py

import wtforms
from wtforms.validators import Email, Length, EqualTo,InputRequired
from models import UserModel, EmailCaptchaModel

class QuestionForm(wtforms.Form):
    title = wtforms.StringField(validators=[Length(min=3, max=100, message='标题长度在3-100之间')])
    content = wtforms.StringField(validators=[Length(min=5, message='内容至少需要5个字符')])


class AnswerForm(wtforms.Form):
    content = wtforms.StringField(validators=[Length(min=5, message='内容至少需要5个字符')])
    question_id = wtforms.IntegerField(validators=[InputRequired(message='必须指明属于哪个问题')])

qa.py

from flask import Blueprint, render_template, request, redirect, url_for, g
from .forms import QuestionForm, AnswerForm
from models import QuestionModel, AnswerModel
from exts import db
# 导入自定义装饰器
from decorators import login_required

bp = Blueprint("qa", __name__, url_prefix="/")

@bp.route("/")
def index():
    questions = QuestionModel.query.order_by(QuestionModel.create_time.desc()).all()
    return render_template('index.html', questions=questions)

@bp.route("/public", methods=['GET', 'POST'])
# 必须登录后才能访问
@login_required
def public():
    if request.method == 'POST':
        form = QuestionForm(request.form)
        if form.validate():
            title = form.title.data
            content = form.content.data
            question = QuestionModel(title=title, content=content, author=g.user)
            db.session.add(question)
            db.session.commit()
            return redirect(url_for('qa.index'))
        else:
            print(form.errors)
    else:
        return render_template('public.html')

🌟问答详情和解答模板

detail.html

{% extends  "base.html" %}
{% block title %}Flask-{{ question.title|truncate(10) }}{% endblock %}
{% block content %}
    <div class="jumbotron">
        <div>
            <h2 class="text-center">{{ question.title }}</h2>
            <p class="lead text-center small">
                作者:{{ question.author.username }}&nbsp&nbsp时间:{{ question.create_time }}</p>
            <hr>
            <p class="text-center">{{ question.content }}</p>

        </div>
        <hr>
        <div>
            <h3>评论({{ question.answers|length }})</h3>
            <form class="form-signin" method="post" action="{{ url_for('qa.answer') }}">
                <div class="form-group">
                    <div class="input-group">
                        <input type="text" class="form-control" name="content"/>
                        <input type="hidden" class="form-control" name="question_id" value="{{ question.id }}"/>
                        <span class="input-group-btn">
                        <button class="btn btn-primary" type="submit">评论</button>
                        </span>
                    </div>
                </div>
            </form>

        </div>
        <hr>
        <div class="bs-example" data-example-id="media-alignment">
            {% for answer in question.answers %}
                <div class="media">
                    <div class="media-left">
                        <img class="img-circle" data-src="holder.js/64x64" alt="64x64"
                             src="{{ url_for('static', filename='img/1.jpg') }}" data-holder-rendered="true"
                             style="width: 64px; height: 64px;">
                    </div>
                    <div class="media-body ">
                        <h5 class="media-heading">{{ answer.author.username }}</h5>
                        <div class="row">
                        <div class="col-md-10"><p>{{ answer.content }}</p></div>
                        <div class="col-md-2">{{ answer.create_time }}</div>
                        </div>
                    </div>
                </div>
            {% endfor %}
        </div>
{% endblock %}	
🌟问答详情和解答视图

qa.py

from flask import Blueprint, render_template, request, redirect, url_for, g
from .forms import QuestionForm, AnswerForm
from models import QuestionModel, AnswerModel
from exts import db
# 导入自定义装饰器
from decorators import login_required

bp = Blueprint("qa", __name__, url_prefix="/")

@bp.route('/detail/<question_id>')
def detail(question_id):
    question = QuestionModel.query.get(question_id)
    return render_template('detail.html', question=question)


@bp.route('/answer/public', methods=["post"])
@login_required
def answer():
    form = AnswerForm(request.form)
    if form.validate():
        content = form.content.data
        # question_id = request.form.get('question_id')
        question_id = form.question_id.data
        answer = AnswerModel(content=content, question_id=question_id, author_id=g.user.id)
        db.session.add(answer)
        db.session.commit()
        return redirect(url_for('qa.detail', question_id=question_id))
    else:
        print(form.errors)
        return redirect(url_for('qa.detail', question_id=request.form.get('question_id')))
🌟搜索功能

搜索功能的视图是index.html,但表单提交是在base.html中们需要注意下表单提交的地址

base.html

<form class="navbar-form navbar-left" method="GET" action="{{ url_for('qa.search') }}">
    <div class="form-group">
        <input type="search" class="form-control" name="search" placeholder="Search">
    </div>
    <button type="submit" class="btn btn-default">搜索</button>
</form>

qa.py

from flask import Blueprint, render_template, request, redirect, url_for, g
from .forms import QuestionForm, AnswerForm
from models import QuestionModel, AnswerModel
from exts import db
# 导入自定义装饰器
from decorators import login_required

bp = Blueprint("qa", __name__, url_prefix="/")
@bp.route('/search')
def search():
    q= request.args.get('search')
    questions = QuestionModel.query.filter(QuestionModel.title.contains(q)).all()
    return render_template('index.html', questions=questions)
🌟总视图代码

qa.py

from flask import Blueprint, render_template, request, redirect, url_for, g
from .forms import QuestionForm, AnswerForm
from models import QuestionModel, AnswerModel
from exts import db
# 导入自定义装饰器
from decorators import login_required

bp = Blueprint("qa", __name__, url_prefix="/")


@bp.route("/")
def index():
    questions = QuestionModel.query.order_by(QuestionModel.create_time.desc()).all()
    return render_template('index.html', questions=questions)


@bp.route("/public", methods=['GET', 'POST'])
@login_required
def public():
    if request.method == 'POST':
        form = QuestionForm(request.form)
        if form.validate():
            title = form.title.data
            content = form.content.data
            question = QuestionModel(title=title, content=content, author=g.user)
            db.session.add(question)
            db.session.commit()
            return redirect(url_for('qa.index'))
        else:
            print(form.errors)
    else:
        return render_template('public.html')


@bp.route('/detail/<question_id>')
def detail(question_id):
    question = QuestionModel.query.get(question_id)
    return render_template('detail.html', question=question)


@bp.route('/answer/public', methods=["post"])
@login_required
def answer():
    form = AnswerForm(request.form)
    if form.validate():
        content = form.content.data
        # question_id = request.form.get('question_id')
        question_id = form.question_id.data
        answer = AnswerModel(content=content, question_id=question_id, author_id=g.user.id)
        db.session.add(answer)
        db.session.commit()
        return redirect(url_for('qa.detail', question_id=question_id))
    else:
        print(form.errors)
        return redirect(url_for('qa.detail', question_id=request.form.get('question_id')))

@bp.route('/search')
def search():
    q= request.args.get('search')
    questions = QuestionModel.query.filter(QuestionModel.title.contains(q)).all()
    return render_template('index.html', questions=questions)

效果:

在这里插入图片描述
在这里插入图片描述

8.【总结】

博主是已经学习了很多Django知识,再来学习Flask知识的,所以了解起来很快,本次文档是跟着2025版-零基础玩转Python Flask框架-学完可就业_哔哩哔哩_bilibili这个视频学Flask基础知识并完成问答项目
博主觉得这个Flask课程完全是可以快速入门Flask框架的,而且视频博主也很贴心,有很多bug都是一起带着大家解决。
Flask完结👏👏🥳🥳

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

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

相关文章

go语法大赏

前些日子单机房稳定性下降&#xff0c;找了好一会才找到真正的原因。这里面涉及到不少go语法细节&#xff0c;正好大家一起看一下。 一、仿真代码 这是仿真之后的代码 package mainimport ("fmt""go.uber.org/atomic""time" )type StopSignal…

软件工程各种图总结

目录 1.数据流图 2.N-S盒图 3.程序流程图 4.UML图 UML用例图 UML状态图 UML时序图 5.E-R图 首先要先了解整个软件生命周期&#xff1a; 通常包含以下五个阶段&#xff1a;需求分析-》设计-》编码 -》测试-》运行和维护。 软件工程中应用到的图全部有&#xff1a;系统…

AAAI2024 | 基于特征多样性对抗扰动攻击 Transformer 模型

Attacking Transformers with Feature Diversity Adversarial Perturbation 摘要-Abstract引言-Introduction相关工作-Related Work方法-Methodology实验-Experiments结论-Conclusion 论文链接 本文 “Attacking Transformers with Feature Diversity Adversarial Perturbatio…

关于数据湖和数据仓的一些概念

一、前言 随着各行业数字化发展的深化,数据资产和数据价值已越来越被深入企业重要发展的战略重心,海量数据已成为多数企业生产实际面临的重要问题,无论存储容量还是成本,可靠性都成为考验企业数据治理的考验。本文来看下海量数据存储的数据湖和数据仓,数据仓库和数据湖,…

常用的Java工具库

1. Collections 首先是 java.util 包下的 Collections 类。这个类主要用于操作集合&#xff0c;我个人非常喜欢使用它。以下是一些常用功能&#xff1a; 1.1 排序 在工作中&#xff0c;经常需要对集合进行排序。让我们看看如何使用 Collections 工具实现升序和降序排列&…

R S的EMI接收机面板

图片摘自R & S官网。 根据您提供的第一张图&#xff08;设备前面板带屏幕的图像&#xff09;&#xff0c;这是 Rohde & Schwarz ESRP7 EMI Test Receiver 的正面显示界面&#xff0c;我将对屏幕上显示的参数逐项进行解读&#xff1a; &#x1f5a5;️ 屏幕参数解读 左…

[ctfshow web入门] web122

信息收集 这一题把HOME开放了&#xff0c;把#和PWD给过滤了 <?php error_reporting(0); highlight_file(__FILE__); if(isset($_POST[code])){$code$_POST[code];if(!preg_match(/\x09|\x0a|[a-z]|[0-9]|FLAG|PATH|BASH|PWD|HISTIGNORE|HISTFILESIZE|HISTFILE|HISTCMD|US…

Java虚拟机 - JVM与Java体系结构

Java虚拟机 JVM与Java体系结构为什么要学习JVMJava与JVM简介Java 语言的核心特性JVM&#xff1a;Java 生态的基石JVM的架构模型基于栈的指令集架构&#xff08;Stack-Based&#xff09;基于寄存器的指令集架构&#xff08;Register-Based&#xff09;JVM生命周期 总结 JVM与Jav…

灌区量测水自动化监测解决方案

一、方案背景 随着社会发展和人口增长&#xff0c;水资源需求不断增大。我国水资源总量虽然丰富&#xff0c;但时空分布不均&#xff0c;加之农业用水占比大且效率偏低&#xff0c;使得水资源短缺问题日益凸显。农业用水一直是我国的耗水大户&#xff0c;占全部耗水总量的60%以…

界面控件DevExpress WinForms v24.2 - 数据处理功能增强

DevExpress WinForms拥有180组件和UI库&#xff0c;能为Windows Forms平台创建具有影响力的业务解决方案。DevExpress WinForms能完美构建流畅、美观且易于使用的应用程序&#xff0c;无论是Office风格的界面&#xff0c;还是分析处理大批量的业务数据&#xff0c;它都能轻松胜…

Linux的MySQL头文件和找不到头文件问题解决

头文件 #include <iostream> #include <mysql_driver.h> #include <mysql_connection.h> #include <cppconn/statement.h> #include <cppconn/resultset.h> #include <cppconn/prepared_statement.h> #include <cppconn/exception.h&g…

wps excel将表格输出pdf时所有列在一张纸上

记录&#xff1a;wps excel将表格输出pdf时所有列在一张纸上 1&#xff0c;调整缩放比例&#xff0c;或选择将所有列打印在一页 2&#xff0c;将表格的所有铺满到这套虚线

zabbix7.2最新版本 nginx自定义监控(三) 设置触发器

安装zabbix-get服务 在zabbix-server端口安装zabbix-get服务 [rootlocalhost ~]# dnf install -y zabbix-get Last metadata expiration check: 1:55:49 ago on Wed 14 May 2025 09:24:49 AM CST. Dependencies resolved. Package Architectur…

缓存的相关内容

缓存是一种介于数据永久存储介质与数据应用之间数据临时的存储介质 实用化保存可以有效地减少低俗数据读取的次数 (例如磁盘IO), 提高系统性能 缓存不仅可以用于提高永久性存储介质的数据读取效率&#xff0c;还可以提供临时的数据存储空间 spring boot中提供了缓存技术, 方便…

[ctfshow web入门] web77

信息收集 上一题的读取flag方式不能用了&#xff0c;使用后的回显是&#xff1a;could not find driver 解题 同样的查目录方法 cvar_export(scandir("glob:///*"));die();cforeach(new DirectoryIterator("glob:///*") as $a){echo($a->__toString…

C++学习-入门到精通-【7】类的深入剖析

C学习-入门到精通-【7】类的深入剖析 类的深入剖析 C学习-入门到精通-【7】类的深入剖析一、Time类的实例研究二、组成和继承三、类的作用域和类成员的访问类作用域和块作用域圆点成员选择运算符(.)和箭头成员选择运算符(->)访问函数和工具函数 四、具有默认实参的构造函数重…

主成分分析的应用之sklearn.decomposition模块的PCA函数

主成分分析的应用之sklearn.decomposition模块的PCA函数 一、模型建立整体步骤 二、数据 2297.86 589.62 474.74 164.19 290.91 626.21 295.20 199.03 2262.19 571.69 461.25 185.90 337.83 604.78 354.66 198.96 2303.29 589.99 516.21 236.55 403.92 730.05 438.41 225.80 …

1. Go 语言环境安装

&#x1f451; 博主简介&#xff1a;高级开发工程师 &#x1f463; 出没地点&#xff1a;北京 &#x1f48a; 人生目标&#xff1a;自由 ——————————————————————————————————————————— 版权声明&#xff1a;本文为原创文章&#xf…

IP协议深度解析:互联网世界的核心基石

作为互联网通信的基础协议&#xff0c;IP&#xff08;Internet Protocol&#xff09;承载着全球99%的网络数据流量。本文将深入剖析IP协议的核心特性、工作原理及演进历程&#xff0c;通过技术原理、协议对比和实战案例分析&#xff0c;为您揭示这个数字世界"隐形交通规则…

Oracle DBMS_STATS.GATHER_DATABASE_STATS 默认行为

Oracle DBMS_STATS.GATHER_DATABASE_STATS 默认行为 DBMS_STATS.GATHER_DATABASE_STATS的默认选项究竟是’GATHER’还是’GATHER AUTO’&#xff1f;这个问题非常重要&#xff0c;因为理解默认行为直接影响统计信息收集策略。 一 官方文档确认 根据Oracle 19c官方文档&#…