在 Flask 和其他应用程序之间共享 sqlalchemy 模型 [英] Share sqlalchemy models between flask and other apps

查看:30
本文介绍了在 Flask 和其他应用程序之间共享 sqlalchemy 模型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个正在运行的 Flask 应用程序,它是根据我们在网上和 Miguel Grinberg 的Flask Web 开发"一书中找到的最佳实践组合设置的.

I have a running Flask application that is set up according to a combination of best practices we found online and in Miguel Grinberg's "Flask Web Development" book.

我们现在需要第二个 Python 应用程序,它不是 Web 应用程序,并且需要访问与 Flask 应用程序相同的模型.我们当然想重用相同的模型,这样两个应用都可以从共享代码中受益.

We now need a second Python application, that is NOT a web app, and that needs access to the same models as the Flask application. We wanted to re-use the same models ofcourse, so both apps can benefit from the shared code.

我们已经移除了对flask-sqlalchemy 扩展的依赖(我们之前使用过,当我们只有Flask 应用程序时).并将其替换为 此处描述的 SQLalchemy 声明式扩展,它更简单一些(Flask-SQLalchemy 为标准 SQLAlchemy 添加了一些特定的东西)

We have removed dependencies on the flask-sqlalchemy extention (which we used before, when we had just the Flask application). And replaced it with the SQLalchemy Declarative extension described here, which is a bit simpler (Flask-SQLalchemy adds a few specific things to standard SQLAlchemy)

根据示例,我们在根目录中创建了一个 database.py 文件.在我们的例子中,有两件事与声明式扩展示例不同:我将引擎和会话放在一个类中,因为我们所有的模型都使用 db.session,而不是 db_session,并且我将带有配置值的字典传递给 init(),这样我就可以使用不同的配置从 Flask 和另一个应用程序中重新使用这个 database.py.它看起来像这样:

In line with the example we have created a database.py file in the root. In our case there are two things different from the Declarative extension example: I put the engine and session in a class, because all of our models use db.session, instead of db_session, and I pass a dictionary with configuration values to the init(), so that I can re-use this database.py from both Flask as well as another application, using a different configuration. it looks like this:

from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base


class Database(object):

    def __init__(self, cfg):
        self.engine = create_engine(cfg['SQLALCHEMY_DATABASE_URI'], convert_unicode=True)
        self.session = scoped_session(sessionmaker(autocommit=False, autoflush=False, bind=self.engine))

    class Model(object):
        pass

Base = declarative_base()

所以现在我们来解决实际问题.Flask 创建一个包含配置选项的类似字典的对象,并将它们作为属性添加到应用程序实例中.它从 实例文件夹config.py 位于站点根目录和环境变量中.我需要从 Flask 传入配置字典,所以我需要 Flask 首先加载和组装配置,然后初始化数据库,并在应用程序文件的根目录中有一个(配置的)db 对象.但是,我们遵循应用工厂模式,因此我们可以针对不同的情况(测试、生产、开发).

So now we come to the actual problem. Flask creates a dictionary-like object containing configuration options, and adds them as a property to the app-instance. It loads them from an instance folder, a config.py in the root of the site and from environment variables. I need to pass in the configuration dictionary from Flask, so I need Flask to FIRST load and assemble the configuration, and after that initialise the database, and have a (configured) db object in the root of the app file. However, we follow the Application factory pattern, so we can use different configurations for different situations (test, production, development).

这意味着我们的 app/__init__.py 看起来像这样(简化):

This means our app/__init__.py looks something like this (simplified):

from flask import Flask
from database import Database
from flask.ext.mail import Mail
from flask_bcrypt import Bcrypt
from config import config

mail = Mail()
bcrypt = Bcrypt()


def create_app(config_name):

    app = Flask(__name__, instance_relative_config=True)

    if not config_name:
        config_name = 'default'
    app.config.from_object(config[config_name])
    app.config.from_pyfile('config.py')
    config[config_name].init_app(app)

    db = Database(app.config)

    mail.init_app(app)
    bcrypt.init_app(app)

    @app.teardown_appcontext
    def shutdown_session(exception=None):
        db.session.remove()

    from main import main as main_blueprint
    app.register_blueprint(main_blueprint)

    return app

但是数据库(模型从 .. 导入的)现在需要在 create_app() 函数中,因为 Flask 就是在那里加载配置.如果我在 create_app() 函数之外实例化 db 对象,它将可以从模型中导入,但它没有被配置!

But the db (that the models import from ..), now needs to be inside the create_app() function, because that's where Flask loads the configuration. If I would instantiate the db object outside of the create_app() function, it will be importable from the models, but it is not configured!

一个示例模型看起来像这样,正如您所看到的,它需要在应用程序的根目录中有一个db":

an example model looks like this, and as you can see, it expects a "db" in the root of the app:

from . base_models import areas
from sqlalchemy.orm import relationship, backref
from ..utils.helper_functions import newid
from .. import db


class Areas(db.Model, areas):
    """Area model class.
    """
    country = relationship("Countries", backref=backref('areas'))

    def __init__(self, *args, **kwargs):
        self.area_id = newid()
        super(Areas, self).__init__(*args, **kwargs)

    def __str__(self):
        return u"{}".format(self.area_name).encode('utf8')

    def __repr__(self):
        return u"<Area: '{}'>".format(self.area_name).encode('utf8')

所以我的问题是,我怎样才能拥有一个可以在外部(通过 Flask 或其他应用程序)配置的数据库实例,并且仍然使用应用程序工厂模式?

So my question is, how can I have a db instance that can be configured externally (by either Flask or another app), and still use the Application Factory Pattern?

代码示例不正确,它有一个 Flask-SQLalchemy 的导入,它被替换为 from database import Database.如有任何混淆,敬请原谅.

edit: The code-example was incorrect, it had an import for Flask-SQLalchemy which was replaced by from database import Database. Sorry for any confusion.

推荐答案

与大多数 Flask 扩展一样,Flask-SQLAlchemy 扩展应该在工厂外创建,然后在工厂中使用 init_app 进行初始化.这样您就可以在创建应用程序之前使用 db 对象.

The Flask-SQLAlchemy extension, like most Flask extensions, should be created outside the factory, then initialized in the factory using init_app. This is so that you can use the db object before an app is created.

db = SQLAlchemy()

def create_app():
    app = Flask(__name__)
    db.init_app(app)
    return app

您的 Flask 应用程序,就像任何设计合理的 Python 项目一样,应该是一个可安装的包.这很简单:确保您的项目布局合理,然后添加一个基本的 setup.py 文件.

Your Flask app, like any properly designed Python project, should be an installable package. This is simple to do: make sure your project layout makes sense, then add a basic setup.py file.

project/
    my_flask_package/
        __init__.py  # at the most basic, this contains create_app and db
    setup.py

from setuptools import setup, find_packages

setup(
    name='my_flask_package',
    version='1.0',
    packages=find_packages(),
    install_requires=['flask', 'flask-sqlalchemy'],
)

$ python setup.py sdist

现在您可以安装 Flask 应用程序及其数据库,以便在其他项目中使用.在您的第二个项目的 virtualenv 中安装并导入它,然后创建并推送一个应用程序以对其进行初始化.

Now you can install your Flask app, along with it's database, for use in other projects. Install and import it in your second project's virtualenv, then create and push an app to initialize it.

$ pip install my_flask_package-1.0.tar.gz

from my_flask_package import db, create_app
create_app().app_context().push()
db.session.query(...)

如果您担心创建应用程序所涉及的开销,您可以向 create_app 函数添加参数以控制初始化的内容.不过,在大多数情况下,这应该不是问题.

If you're concerned about overhead involved in creating your application, you could add arguments to the create_app function to control what gets initialized. For most cases this shouldn't be an issue though.

这篇关于在 Flask 和其他应用程序之间共享 sqlalchemy 模型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆