Flask快速入门和问答项目源码
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 herereturn '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 herereturn '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_templateapp = 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_templateapp = 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_templateapp = 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>
结果如下:
当然这是一个图片的案例,如果想引入css
、js
等或者其他文件内容的话,将filename='img/1.jpg'
的值替换一下就可以
其中,模板中也有过滤器的使用,可以自行网上查找,和django类似
6.【数据库连接】
6.1.安装模块
需要安装两个模块 flask_sqlalchemy
和pymysql
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 SQLAlchemyapp = 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 herereturn '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 SQLAlchemyapp = 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 SQLAlchemyapp = 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、访问端口号portapp.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 datetimeclass UserModel(db.Model):"""用户表"""__tablename__ = 'user'# id:主键primary_key=True,自增:autoincrement=Trueid = 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 Messagebp = 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.codeif (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 dbclass 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.datauser = UserModel.query.filter_by(email=email).first()if user:raise wtforms.ValidationError(message='邮箱已经被注册')# 验证码是否正确def validate_captcha(self, field):captcha = field.dataemail = self.email.datacaptcha_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_hashbp = 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.datapassword = form.password.datauser = 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.idreturn 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.datausername = form.username.datapassword = 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 }}     </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_fordef 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, EmailCaptchaModelclass 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_requiredbp = 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.datacontent = form.content.dataquestion = 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 }}  时间:{{ 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_requiredbp = 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.dataanswer = 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_requiredbp = 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_requiredbp = 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.datacontent = form.content.dataquestion = 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.dataanswer = 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完结👏👏🥳🥳
相关文章:

Flask快速入门和问答项目源码
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模…...

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

软件工程各种图总结
目录 1.数据流图 2.N-S盒图 3.程序流程图 4.UML图 UML用例图 UML状态图 UML时序图 5.E-R图 首先要先了解整个软件生命周期: 通常包含以下五个阶段:需求分析-》设计-》编码 -》测试-》运行和维护。 软件工程中应用到的图全部有:系统…...
R-tree详解
R-tree 是一种高效的多维空间索引数据结构,专为快速检索空间对象(如点、线、区域)而设计。它广泛应用于地理信息系统(GIS)、计算机图形学、数据库等领域,支持范围查询、最近邻搜索等操作。以下是其核心原理…...

AAAI2024 | 基于特征多样性对抗扰动攻击 Transformer 模型
Attacking Transformers with Feature Diversity Adversarial Perturbation 摘要-Abstract引言-Introduction相关工作-Related Work方法-Methodology实验-Experiments结论-Conclusion 论文链接 本文 “Attacking Transformers with Feature Diversity Adversarial Perturbatio…...

关于数据湖和数据仓的一些概念
一、前言 随着各行业数字化发展的深化,数据资产和数据价值已越来越被深入企业重要发展的战略重心,海量数据已成为多数企业生产实际面临的重要问题,无论存储容量还是成本,可靠性都成为考验企业数据治理的考验。本文来看下海量数据存储的数据湖和数据仓,数据仓库和数据湖,…...
鸿蒙OSUniApp制作自定义的下拉菜单组件(鸿蒙系统适配版)#三方框架 #Uniapp
UniApp制作自定义的下拉菜单组件(鸿蒙系统适配版) 前言 在移动应用开发中,下拉菜单是一个常见且实用的交互组件,它能在有限的屏幕空间内展示更多的选项。虽然各种UI框架都提供了下拉菜单组件,但在一些特定场景下&…...
C++面试2——C与C++的关系
C与C++的关系及核心区别的解析 一、哲学与编程范式:代码组织的革命 过程式 vs 多范式混合 C语言是过程式编程的典范,以算法流程为中心,强调“怎么做”(How)。例如,实现链表操作需手动管理节点指针和内存。 C++则是多范式语言,支持面向对象(OOP)、泛型编程(模板)、函…...

常用的Java工具库
1. Collections 首先是 java.util 包下的 Collections 类。这个类主要用于操作集合,我个人非常喜欢使用它。以下是一些常用功能: 1.1 排序 在工作中,经常需要对集合进行排序。让我们看看如何使用 Collections 工具实现升序和降序排列&…...
基于LabVIEW的双音多频系统设计
目录 1 系统设计概述 双音多频(Dual-Tone Multi-Frequency, DTMF)信号是一种广泛应用于电话系统中的音频信号,通过不同的频率组合表示不同的按键。每个按键对应两个频率,一个低频和一个高频,共同组成独特的信号。在虚拟仪器技术快速发展的背景下,利用LabVIEW等图形化编程…...

R S的EMI接收机面板
图片摘自R & S官网。 根据您提供的第一张图(设备前面板带屏幕的图像),这是 Rohde & Schwarz ESRP7 EMI Test Receiver 的正面显示界面,我将对屏幕上显示的参数逐项进行解读: 🖥️ 屏幕参数解读 左…...

[ctfshow web入门] web122
信息收集 这一题把HOME开放了,把#和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…...
Nginx+Lua 实战避坑:从模块加载失败到版本冲突的深度剖析
Nginx 集成 Lua (通常通过 ngx_http_lua_module 或 OpenResty) 为我们提供了在 Web 服务器层面实现动态逻辑的强大能力。然而,在享受其高性能和灵活性的同时,配置和使用过程中也常常会遇到各种令人头疼的问题。本文将结合实际案例,深入分析在 Nginx+Lua 环境中常见的技术问题…...
LangChain框架-Chain 链详解
摘要 本文基于源码分析与官方文档梳理,系统解析 LangChain 框架中的核心组件 Chain 链,旨在帮助开发者深入理解其设计原理、功能分类及实践应用场景。 作为 LangChain 的核心机制,Chain 链采用管道-过滤器(Pipe-Filter)…...

Java虚拟机 - JVM与Java体系结构
Java虚拟机 JVM与Java体系结构为什么要学习JVMJava与JVM简介Java 语言的核心特性JVM:Java 生态的基石JVM的架构模型基于栈的指令集架构(Stack-Based)基于寄存器的指令集架构(Register-Based)JVM生命周期 总结 JVM与Jav…...
elementUI调整滚动条高度后与固定列冲突问题解决
/* 1. 首先确保基础样式生效 */ .el-table.el-table–scrollable-x .el-table__body-wrapper { overflow-x: auto !important; } /* 2. 设置滚动条高度(对所有表格生效) */ .el-table__body-wrapper::-webkit-scrollbar { height: 10px !important; } …...
基于 nvitop+Prometheus+Grafana 的物理资源与 VLLM 引擎服务监控方案
一、方案背景与目标 在人工智能与高性能计算场景中,对物理资源(尤其是 GPU)的实时监控以及对 VLLM 引擎服务的性能追踪至关重要。本方案通过整合 nvitop、Prometheus 和 Grafana 三大组件,构建一套完整的监控体系,实现…...
互联网大厂Java求职面试:Spring AI与大模型交互在短视频平台中的应用
互联网大厂Java求职面试:Spring AI与大模型交互在短视频平台中的应用 面试场景设定 郑薪苦,一名有着丰富项目经验但总是能用奇葩比喻解释复杂技术的程序员,正在接受某知名互联网大厂技术总监的面试。 第一轮提问 面试官:假设我…...
【Lua】java 调用redis执行 lua脚本
【Lua】java 调用redis执行 lua脚本 public Object executeLuaScript(String script, List<String> keys, Object... args) {// 注意: 这里 Long.class 是返回值类型, 一定要指定清楚 不然会报错return this.redisTemplate.execute(RedisScript.of(j脚本, Long.class), k…...
【工奥阀门科技有限公司】签约智橙PLM
近日,工奥阀门科技有限公司正式签约了智橙泵阀行业版PLM。 忠于质量,臻于服务,精于研发 工奥阀门科技有限公司(以下简称工奥阀门)坐落于浙江永嘉,是一家集设计、开发、生产、销售、安装、服务为一体的阀门…...

灌区量测水自动化监测解决方案
一、方案背景 随着社会发展和人口增长,水资源需求不断增大。我国水资源总量虽然丰富,但时空分布不均,加之农业用水占比大且效率偏低,使得水资源短缺问题日益凸显。农业用水一直是我国的耗水大户,占全部耗水总量的60%以…...
SpringBoot整合MQTT实战:基于EMQX构建高可靠物联网通信,从零到一实现设备云端双向对话
一、引言 随着物联网(IoT)技术的快速发展,MQTT(Message Queuing Telemetry Transport)协议因其轻量级、低功耗和高效的特点,已成为物联网设备通信的事实标准。本文将详细介绍如何使用SpringBoot框架整合MQTT协议,基于开源MQTT代理EMQX实现设…...
AI与机器学习深度集成:从设备端能力爆发到开发工具智能化
简介 AI与机器学习技术正以惊人的速度在移动开发领域深入集成,设备端AI能力爆发与AI辅助开发工具的崛起,为开发者带来了前所未有的高效开发体验和应用创新机遇。本文将全面解析Google最新AI技术栈(包括ML Kit 2.0和Gemini Nano模型)的特性与应用场景,探索Android Studio …...

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

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时所有列在一张纸上
记录:wps excel将表格输出pdf时所有列在一张纸上 1,调整缩放比例,或选择将所有列打印在一页 2,将表格的所有铺满到这套虚线...

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…...
CDN加速对云手机延迟的影响
一、CDN加速对云手机延迟的核心作用 缩短物理距离,降低网络延迟 CDN通过全球分布的节点,将云手机的服务内容(如应用数据、画面流)缓存至离用户最近的服务器,减少数据传输的物理距离。例如,用户在中国访问美…...
为什么 Docker 建议关闭 Swap
在使用 Docker 时,关闭系统 Swap(交换分区) 是一个常见的推荐做法,尤其是在生产环境中。虽然 Docker 不强制要求禁用 Swap,但出于性能、稳定性、可控性和资源管理的目的,通常建议这样做。 为什么 Docker 建…...

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