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