flask框架--flask项目之模块划分
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")
posted on 2022-02-21 18:36 wenbin_ouyang 阅读(1166) 评论(0) 编辑 收藏 举报