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完结👏👏🥳🥳