Flask-RESTful 项目结构

5

这是我正在使用的:

/myproject
    README.md
    runserver.py
    /myproject
        __init__.py
        api.py
        /resources
            __init__.py
            foo.py
            bar.py
        /common
            __init__.py
            db.py
    /tests
        test_myproject.py

这里没有什么特别的。大部分内容可以在Flask-RESTful用户指南的Intermediate Usage页面中找到。

我的关注点是循环导入...

api.py

from flask import Flask
from flask_restful import Api

app = Flask(__name__)

from myproject.resources.foo import Foo
from myproject.resources.bar import Bar

api = Api(app)

api.add_resource(Foo, '/Foo', '/Foo/<str:id>')
api.add_resource(Bar, '/Bar', '/Bar/<str:id>')

foo.py

from flask_restful import Resource
from myproject.common.db import query_db


class Foo(Resource):
    def get(self):
        pass
    def post(self):
        pass

db.py

from flask import g
import sqlite3
from myproject.api import app


def get_db():
    db = getattr(g, '_database', None)
    if db is None:
        db = g._database = sqlite3.connect(app.config['DATABASE'])
        db.row_factory = make_dicts
    return db


def query_db(query, args=(), one=False):
    cur = get_db().execute(query, args)
    rv = cur.fetchall()
    cur.close()
    return (rv[0] if rv else None) if one else rv


@app.teardown_appcontext
def close_connection(exception):
    db = getattr(g, '_database', None)
    if db is not None:
        db.commit()
        db.close()

显然,我在项目中引入了循环导入:

api.py -> foo.py -> db.py -> api.py

只要在导入资源之前实例化Flask应用程序对象(我已经这样做了),这不是一个大问题。类似的模式在这里讨论(请参见页面底部的循环导入部分)。 我的问题是... 这是一种构建Flask-RESTful项目的好方法吗? 这里是关于此主题的最接近的SO问题。我对提供的答案不满意,因为我不想将数据库函数保留在顶层__init__.py文件中(或在路由所属的api.py中)。
以下是几个类似的SO问题,但它们处理导入错误(我没有遇到这些问题): 结构Flask-Restful API以使用SQLAlchemy

尝试运行应用程序时导入模型出错

1个回答

2

由于您的问题在很大程度上是基于个人意见的,所以我会建议我认为更好的解决方案:)

不要在db.py中导入myproject.api.app,而是在db.py中创建自己的模块级全局变量来存储数据库配置:

db.py

from flask import g
import sqlite3

_db_config = None # Holds database config

def init(app):
    """ Function must be called to initalize this module """
    global _db_config
    global close_connection
    _db_config = app.config['DATABASE']
    # Manually apply @app.teardown_appcontext decorator
    close_connection = app.teardown_appcontext(close_connection)


def _db_connect():
    if _db_config is None:
        raise Exception('Call init first') # or whatever error you want
    return sqlite3.connect(_db_config)


def get_db():
    db = getattr(g, '_database', None)
    if db is None:
        db = g._database = _db_connect()
        db.row_factory = make_dicts
    return db

....

def close_connection(exception):
    db = getattr(g, '_database', None)
    if db is not None:
        db.commit()
        db.close()

然后在api.py中通过调用myproject.common.db.init()来初始化数据库。

api.py

from flask import Flask
from flask_restful import Api
from myproject.common import db

app = Flask(__name__)
db.init(app)

....

这样,db.py 就不再依赖于 api.py 了。


1
感谢您的回复。我喜欢您的解决方案,但是db.py仍然依赖于api.py。请查看带有@app.teardown_appcontext装饰器的close_connection函数。 - jamesdarabi
1
如果您不介意手动应用装饰器,那么这个问题可以很容易地解决。请参见我上面的编辑。 - junnytony
1
首先,我认为您需要在init函数中将close_connection声明为全局变量,否则Python会将其视为局部变量。其次,当我尝试使用此方法时,我收到以下错误:ProgrammingError: Cannot operate on a closed database. - jamesdarabi
1
好的,已更正。您能追踪ProgrammingError的位置吗? - junnytony
嘿,runserver.py 怎么样? - Saravanan Nandhan
哇,这么多不同的变体。我很困惑,哪一个是Flask的正确结构。 - greendino

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接