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>

結果如下:

當然這是一個圖片的案例,如果想引入cssjs等或者其他文件內容的話,將filename='img/1.jpg'的值替換一下就可以

其中,模板中也有過濾器的使用,可以自行網上查找,和django類似

6.【數據庫連接】

6.1.安裝模塊

需要安裝兩個模塊 flask_sqlalchemypymysql

pip install flask_sqlalchemy
pip install pymysql    

6.2.創建數據庫并測試連接

創建數據庫

 create database flask_test default charset="utf8";

測試連接 ---- app.py

from flask import Flask, request, render_template
from flask_sqlalchemy import 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 }}  &nbsp&nbsp&nbsp  </span> {{ question.create_time }}</p></li>{% endfor %}</ul></div></div>
{% endblock %}

public.html

{% extends  'base.html' %}
{% block title %}發布問答{% endblock %}
{% block content %}<div class="panel panel-warning"><div class="panel-heading"><h3 class="text-center">發布問答</h3></div><div class="panel-body"><form class="form-signin" method="post"><div class="form-group"><label for="title">標題:</label><input type="text" class="form-control" name="title" id="title" placeholder="請輸入標題"></div><div class="form-group"><label for="content">內容:</label><textarea name="content" rows="14" cols="60" class="form-control" id="content" placeholder="請輸入內容"></textarea></div><div class="text-right"><button type="submit" class="btn btn-primary ">發布</button></div></form></div></div>
{% endblock %}

🌟問答和主頁視圖

這里有一個權限控制需要注意下,自定義權限,裝飾器@login_required

存放在decorators.py

from functools import wraps
from flask import g, redirect, url_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 }}&nbsp&nbsp時間:{{ question.create_time }}</p><hr><p class="text-center">{{ question.content }}</p></div><hr><div><h3>評論({{ question.answers|length }})</h3><form class="form-signin" method="post" action="{{ url_for('qa.answer') }}"><div class="form-group"><div class="input-group"><input type="text" class="form-control" name="content"/><input type="hidden" class="form-control" name="question_id" value="{{ question.id }}"/><span class="input-group-btn"><button class="btn btn-primary" type="submit">評論</button></span></div></div></form></div><hr><div class="bs-example" data-example-id="media-alignment">{% for answer in question.answers %}<div class="media"><div class="media-left"><img class="img-circle" data-src="holder.js/64x64" alt="64x64"src="{{ url_for('static', filename='img/1.jpg') }}" data-holder-rendered="true"style="width: 64px; height: 64px;"></div><div class="media-body "><h5 class="media-heading">{{ answer.author.username }}</h5><div class="row"><div class="col-md-10"><p>{{ answer.content }}</p></div><div class="col-md-2">{{ answer.create_time }}</div></div></div></div>{% endfor %}</div>
{% endblock %}	
🌟問答詳情和解答視圖

qa.py

from flask import Blueprint, render_template, request, redirect, url_for, g
from .forms import QuestionForm, AnswerForm
from models import QuestionModel, AnswerModel
from exts import db
# 導入自定義裝飾器
from decorators import login_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完結👏👏🥳🥳

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/80485.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/80485.shtml
英文地址,請注明出處:http://en.pswp.cn/web/80485.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

flutter長列表 ListView、GridView、SingleChildScrollView、CustomScrollView區別

組件名稱用途/適合場景是否懶加載支持列表結構用法復雜度SingleChildScrollView適用于內容數量不大、不重復的頁面&#xff08;如表單、靜態內容&#xff09;? 否? 否??ListView適用于垂直方向的長列表&#xff0c;自動滾動&#xff1b;適合展示大量數據? 支持? 是??Li…

鴻蒙OSUniApp 開發的一鍵分享功能#三方框架 #Uniapp

使用 UniApp 開發的一鍵分享功能 在移動應用開發中&#xff0c;分享功能幾乎是必不可少的一環。一個好的分享體驗不僅能帶來更多用戶&#xff0c;還能提升產品的曝光度。本文將詳細講解如何在 UniApp 框架下實現一個簡單高效的一鍵分享功能&#xff0c;適配多個平臺。 各平臺分…

Vue-監聽屬性

監聽屬性 簡單監聽 點擊切換名字&#xff0c;來回變更Tom/Jerry&#xff0c;輸出 你好&#xff0c;Tom/Jerry 代碼 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><title>監聽屬性</title><!-- …

DeepSeek 賦能物聯網:從連接到智能的跨越之路

目錄 一、引言&#xff1a;物聯網新時代的開啟二、DeepSeek 技術揭秘2.1 DeepSeek 是什么2.2 DeepSeek 技術優勢 三、DeepSeek 與物聯網的融合之基3.1 物聯網發展現狀與挑戰3.2 DeepSeek 帶來的變革性突破 四、DeepSeek 在物聯網的多元應用場景4.1 智慧電力&#xff1a;開啟能源…

3.6/Q1,GBD數據庫最新文章解讀

文章題目&#xff1a;Global, regional, and national burden of geriatric depressive disorders in people aged 60 years and older: an analysis of the Global Burden of Disease Study 2021 DOI&#xff1a;10.1186/s12991-025-00560-2 中文標題&#xff1a;60 歲及以上人…

LVGL學習筆記

文章目錄 一、 LVGL移植教程(GD32)一 并行驅動 LED二三一、 LVGL移植教程(GD32) 參考鏈接 1.GD32+LVGL移植教程(超詳細)——基于GD32F303X系列MCU 一 并行驅動 LED 根據您提供的引腳信號(DCLK、DISP、HSYNC、VSYNC、DE),可以判斷這是一款采用 TTL/Parallel RGB 接口…

軟件架構之--論微服務的開發方法1

論微服務的開發方法1 摘要 2023年 2月,本人所在集團公司承接了長三角地區某省漁船圖紙電子化審查系統項目開發,該項目旨在為長三角地區漁船建造設計院、以及漁船圖紙審查機構提供一個便捷的漁船圖紙電子化審查服務平臺。在此項目中,我作為項目組成員參與項目的建設工作,并…

如何在終端/命令行中把PDF的每一頁轉換成圖片(PNG)

今天被對象安排了一個任務&#xff1a; 之前自己其實也有這個需要&#xff0c;但是吧&#xff0c;我懶&#xff1a;量少拖拽&#xff0c;量大就放棄。但這次躲不過去了&#xff0c;所以研究了一下有什么工具可以做到這個需求。 本文記錄我這次發現的使用 XpdfReader 的方法。…

mac安裝cast

背景 pycharm本地運行腳本時提示cast沒有安裝 問題原因 腳本嘗試調用cast命令&#xff08;以太坊開發工具foundry中的子命令&#xff09;&#xff0c;但您的系統未安裝該工具。 從日志可見&#xff0c;錯誤發生在通過sysutil.py執行shell命令時。 解決方案 方法1&#xf…

【搭建Node-RED + MQTT Broker實現AI大模型交互】

搭建Node-RED MQTT Broker實現AI大模型交互 搭建Node-RED MQTT Broker實現AI大模型交互一、系統架構二、環境準備與安裝1. 安裝Node.js2. 安裝Mosquitto MQTT Broker3. 配置Mosquitto4. 安裝Node-RED5. 配置Node-RED監聽所有網絡接口6. 啟動Node-RED 三、Node-RED流程配置1. …

算法第21天 | 第77題. 組合、216. 組合總和 III、17. 電話號碼的字母組合

回溯基礎概念 什么是回溯&#xff1f; 如何實現回溯&#xff1f; 第77題. 組合 題目 思路與解法 carl的講解&#xff1a; 回溯搜索法 class Solution:def combine(self, n: int, k: int) -> List[List[int]]:self.path []self.res []self.backtracking(n, k, 1)retu…

嵌入式硬件篇---拓展板

文章目錄 前言 前言 本文簡單介紹了拓展板的原理以及使用。

【深度學習基礎】從感知機到多層神經網絡:模型原理、結構與計算過程全解析

【深度學習基礎】從感知機到多層神經網絡&#xff1a;模型原理、結構與計算過程全解析 1. 引言 神經網絡的重要性&#xff1a; 作為人工智能的核心技術之一&#xff0c;神經網絡通過模擬人腦神經元的工作機制&#xff0c;成為解決復雜模式識別、預測和決策任務的利器。從圖像分…

sparkSQL讀入csv文件寫入mysql(2)

&#xff08;二&#xff09;創建數據庫和表 接下來&#xff0c;我們去創建一個新的數據庫&#xff0c;數據表&#xff0c;并插入一條數據。 -- 創建數據庫 CREATE DATABASE spark; -- 使用數據庫 USE spark;-- 創建表 create table person(id int, name char(20), age int);-- …

JVM如何處理多線程內存搶占問題

目錄 1、堆內存結構 2、運行時數據 3、內存分配機制 3.1、堆內存結構 3.2、內存分配方式 1、指針碰撞 2、空閑列表 4、jvm內存搶占方案 4.1、TLAB 4.2、CAS 4.3、鎖優化 4.4、逃逸分析與棧上分配 5、問題 5.1、內存分配競爭導致性能下降 5.2、偽共享&#xff08…

Ubuntu---omg又出bug了

自用遇到問題的合集 250518——桌面文件突然消失 ANS&#xff1a;參考博文

正則表達式與文本處理的藝術

引言 在前端開發領域&#xff0c;文本處理是一項核心技能。正則表達式作為一種強大的模式匹配工具&#xff0c;能夠幫助我們高效地處理各種復雜的文本操作任務。 正則表達式基礎 什么是正則表達式&#xff1f; 正則表達式是一種用于匹配字符串中字符組合的模式。它由一系列…

初學c語言15(字符和字符串函數)

一.字符串分類函數 頭文件&#xff1a;ctype.h 作用&#xff1a;判斷是什么類型的字符 函數舉例&#xff1a; 函數 符合條件就為真 islower判斷是否為小寫字符&#xff08;a~z&#xff09;isupper判斷是否為大寫字符&#xff08;A~Z&#xff09;isdigit十進制數字&#xf…

12-串口外設

一、串口外設的基本概述 1、基本定義 串口通信&#xff0c;通過在通信雙方之間以比特位&#xff08;bit&#xff09;的形式逐一發送或接收數據&#xff0c;實現了信息的有效傳遞。其通信方式不僅簡單可靠&#xff0c;而且成本很低。 2、stm32的串口 下面是兩個MCU的數據交互&…

NE555雙音門鈴實驗

1腳為地。通常被連接到電路共同接地。 2腳為觸發輸入端。 3腳為輸出端&#xff0c;輸出的電平狀態受觸發器的控制&#xff0c;而觸發器受上比較器6腳和下比較器2腳的控制。當觸發器接受上比較器A1從R腳輸入的高電平時&#xff0c;觸發器被置于復位狀態&#xff0c;3腳輸出低電…