木心

毕竟几人真得鹿,不知终日梦为鱼

导航

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  阅读(1043)  评论(0编辑  收藏  举报