1、簡單拆分:模型,路由,配置
2、循環依賴問題
3、使用裝飾器解決路由模塊划分問題
4、使用藍圖划分模塊
flask框架--數據庫ORM框架flask-sqlalchemy
我們學習Flask框架,是從單個文件開始,所有的代碼都寫在一個文件中,包括定義路由、視圖函數、定義模型等。但這顯然存在一個問題,隨着業務代碼的增加,將所有代碼都放在單個程序中,是非常不合適的。這不僅會讓代碼閱讀變得困難,而且會給后期維護帶來麻煩。本文基於上面這篇文章第8節的圖書demo,進行模塊划分(將db_demo.py拆開)
1、簡單拆分:模型,路由,配置 <--返回目錄
將db_demmo.py拆分:將模型定義拆分為Author和Book;將路由定義拆分到route.py;將flask的app對象,和 SQLAlchemy的db對象拆分到app.py(方便其他模型引用app和db對象);配置放到configs.py。拆分后結構為:
主啟動類 main.py
from app import app import route if __name__ == "__main__": app.run(host="0.0.0.0", port=8081, debug=True)
app.py 包含 flask的app對象,和 SQLAlchemy的db對象
from flask import Flask import configs from flask_sqlalchemy import SQLAlchemy app = Flask(__name__) # 加載配置文件 app.config.from_object(configs) db = SQLAlchemy(app) # db綁定app db.init_app(app)
配置文件 configs.py
SECRET_KEY = "TEST_SECRET_KEY" # sqlalchemy 的配置 SQLALCHEMY_DATABASE_URI = "mysql://root:123456@127.0.0.1:3306/db1" # 如果設置成 True (默認情況),Flask-SQLAlchemy 將會追蹤對象的修改並且發送信號。這需要額外的內存, 如果不必要的可以禁用它。 SQLALCHEMY_TRACK_MODIFICATIONS = True # 查詢時顯示原始SQL語句 SQLALCHEMY_ECHO = True
路由 route.py
# coding:utf-8 from flask import Flask, render_template, redirect, url_for from flask_wtf import FlaskForm from wtforms import StringField, SubmitField from wtforms.validators import DataRequired from app import db, app from model.Author import Author from model.Book import Book # 定義表單模型類 class AuthorBookForm(FlaskForm): """自定義的表單模型類""" # DataRequired 驗證器:保證數據必填,並且不能為空 author_name = StringField(label="作者", validators=[DataRequired("作者不能為空")]) book_name = StringField(label="書籍", validators=[DataRequired("書籍不能為空")]) submit = SubmitField(label="提交") @app.route("/index", methods = ["get", "post"]) def index(): # 創建表單對象。如果時post請求,前端發送了數據,flask會把數據在構造form對象的時候,存放到對象中 authorBookForm = AuthorBookForm() author_li = Author.query.all() return render_template("author_book.html", authors=author_li, authorBookForm=authorBookForm) @app.route("/add", methods = ["post"]) def add_book(): # 創建表單對象。如果時post請求,前端發送了數據,flask會把數據在構造form對象的時候,存放到對象中 authorBookForm = AuthorBookForm() # 判斷form中的數據是否合理。如果form中的數據完全滿足所有的驗證器,則返回真,否則返回假 if authorBookForm.validate_on_submit(): # 驗證通過 author_name = authorBookForm.author_name.data book_name = authorBookForm.book_name.data print("驗證通過, author_name: {0}, book_name: {1}".format(author_name, book_name)) # 根據作者名查詢, 保存書籍 author = Author.query.filter_by(name=author_name).first() if author == None: auth = Author(name=author_name) db.session.add(auth) db.session.commit() book = Book(name=book_name, author_id = auth.id) db.session.add(book) db.session.commit() else: book = Book(name=book_name, author_id = author.id) db.session.add(book) db.session.commit() return redirect(url_for("index")) @app.route("/delete_book/<book_id>") def delete_book(book_id): print("delete_book book_id: {0}".format(book_id)) if book_id == None: return redirect(url_for("index")) book = Book.query.get(book_id) if book == None: print("查詢不到書籍記錄, book_id: {0}".format(book_id)) else: db.session.delete(book) db.session.commit() return redirect(url_for("index")) @app.route("/delete_author/<author_id>") def delete_author(author_id): print("delete_authro author_id: {0}".format(author_id)) if author_id == None: return redirect(url_for("index")) author = Author.query.get(author_id) if author == None: print("查詢不到指定作者, author_id: {0}".format(author_id)) else: # 先刪除作者下面所有書籍,然后刪除作者 for book in author.books: db.session.delete(book) db.session.delete(author) db.session.commit() return redirect(url_for("index"))
模型類 Author.py
from app import db # 創建模型類 class Author(db.Model): __tablename__ = "tb_author" id = db.Column(db.Integer, primary_key = True) # 整形的主鍵,默認設置為自增主鍵 name = db.Column(db.String(32), unique =True) books = db.relationship("Book", backref = "author")
模型類 Book.py
from app import db class Book(db.Model): __tablename__ = "tb_book" id = db.Column(db.Integer, primary_key = True) # 整形的主鍵,默認設置為自增主鍵 name = db.Column(db.String(64), unique = True) author_id = db.Column(db.Integer, db.ForeignKey("tb_author.id")) # 外鍵,存儲tb_author表的id
2、循環依賴問題 <--返回目錄
main.py
# coding:utf-8 from flask import Flask from user import get_user # 這里發生循環依賴。main.py要導入user模塊,user模塊又要導入main.py app = Flask(__name__) if __name__ == "__main__": print(app.url_map) # 打印路由信息 app.run(host="0.0.0.0", port=8081, debug=True)
from main import app @app.route("/get_user") def get_user(): pass
運行main.py報錯信息
ImportError: cannot import name 'get_user' from partially initialized module 'user' (most likely due to a circular import) (E:\pycode\user.py)
3、使用裝飾器解決路由模塊划分問題 <--返回目錄
使用裝飾器解決路由模塊划分問題:app.route("/get_user")(get_user) 裝飾器就是一個函數,函數傳入的參數為裝飾器裝飾的函數名
main.py
# coding:utf-8 from flask import Flask from user import get_user # 這里發現循環依賴 app = Flask(__name__) app.route("/get_user")(get_user) if __name__ == "__main__": print(app.url_map) # 打印路由信息 app.run(host="0.0.0.0", port=8081, debug=True)
user.py
# from main import app # @app.route("/get_user") def get_user(): pass
4、使用藍圖划分模塊 <--返回目錄
BluePrint藍圖:用於實現單個應用的視圖、模板、靜態文件的集合。藍圖就是模塊處理的類。簡單來說,藍圖就是一個存儲操作路由映射方法的容器,主要用來實現客戶端請求和url相互關聯的功能。在Flask中,使用藍圖可以幫助我們實現模塊化應用的功能。
藍圖的運行機制:藍圖是保存了一組將來可以在應用對象上執行的操作。注冊路由就是一種操作,當在程序實例上調用route裝飾器注冊路由時,這個操作將修改對象的url_map路由映射列表。當我們在藍圖對象上調用route裝飾器注冊路由時,它只是在內部的一個延遲操作記錄列表defered_functions中添加了一個項。當執行應用對象的register_blueprint()方法時,應用對象從藍圖對象的defered_functions列表中取出每一項,即調用應用對象的add_url_rule()方法,這將會修改程序實例的路由映射列表。
藍圖的使用
from flask import Flask from user import app_userss app = Flask(__name__) # 注冊藍圖 app.register_blueprint(app_userss) if __name__ == "__main__": print(app.url_map) # 打印路由信息 app.run(host="0.0.0.0", port=8081, debug=True)
from flask import Blueprint # 創建一個藍圖對象,藍圖就是一個小模塊的抽象概念 app_userss = Blueprint("app_users", __name__) @app_userss.route("/get_user") def get_user(): pass
以目錄形式定義藍圖:在創建藍圖對象時 app_books = Blueprint("app_books", __name__, template_folder="templates") 中 __name__參數指定了這個藍圖對象尋找模板文件的起始點,template_folder="templates") 指定了模板目錄的名稱。
main.py
# coding:utf-8 from flask import Flask from book import app_books app = Flask(__name__) # 注冊藍圖 app.register_blueprint(app_books, url_prefix="/books") if __name__ == "__main__": print(app.url_map) # 打印路由信息 app.run(host="0.0.0.0", port=8081, debug=True)
book目錄下面創建__init__.py,python就可以把book目錄當成一個模塊來使用。
__init__.py
from flask import Blueprint # 創建藍圖對象 app_books = Blueprint("app_books", __name__, template_folder="templates") # 在 __init__.py文件被執行的時候,把視圖加載進來,讓藍圖和應用程序知道有視圖的存在 from .views import get_book
views.py
from . import app_books from flask import render_template @app_books.route("/get_book") def get_book(): return render_template("book.html")