2015-10-14 6 views
12

私はオンラインで見つかったベストプラクティスとMiguel Grinbergの "Flask Web Development"ブックの組み合わせに従って設定されたFlaskアプリケーションを実行しています。フラスコと他のアプリケーションの間でsqlalchemyモデルを共有

Webアプリケーションではなく、Flaskアプリケーションと同じモデルにアクセスする必要がある2番目のPythonアプリケーションが必要になりました。同じモデルを再利用したかったので、両方のアプリケーションが共有コードの恩恵を受けることができます。

フラスコのsqlalchemy拡張(これまでFlaskアプリケーションを使用していたときに使用していた)の依存関係を削除しました。そしてそれを少し簡単なSQLalchemy Declarative extension described hereに置き換えました(Flask-SQLalchemy adds a few specific things to standard SQLAlchemy

この例では、ルートにdatabase.pyファイルを作成しました。私たちの場合、Declarativeの拡張の例とは異なる2つのことがあります。すべてのモデルがdb_sessionではなくdb.sessionを使用し、コンフィグレーション値を持つ辞書をinitに渡すため、エンジンとセッションをクラスに入れます()、私は別の設定を使用して、Flaskと他のアプリケーションの両方からこのdatabase.pyを再利用できるようになりました。

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は設定オプションを含む辞書的なオブジェクトを作成し、プロパティとしてapp-instanceに追加します。サイトのルートにあるinstance folderconfig.py、および環境変数からロードします。私はFlaskから設定辞書を渡す必要があるので、FlaskにFIRSTをロードして設定を組み立て、その後にデータベースを初期化し、アプリケーションファイルのルートに(設定された)dbオブジェクトを置く必要があります。しかし、我々はApplication factory patternに従っているので、さまざまな状況(テスト、生産、開発)に異なる構成を使用することができます。

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()関数内である必要があります:これは私たちのapp/__init__.pyはこのような何か(簡体字)を探します意味

Flaskは設定を読み込みます。 create_app()関数の外でdbオブジェクトをインスタンス化すると、モデルからインポートできますが、設定されていません!

モデル例は次のようになり、そしてあなたが見ることができるように、それはアプリのルートに「デシベル」を期待する:

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') 

だから私の質問は、私がいることを、DBインスタンスを持つことができるか、です外部(Flaskまたは他のアプリのいずれか)で設定でき、アプリケーションファクトリパターンは引き続き使用できますか?

編集:コード例が間違っていた、それはfrom database import Databaseに置き換えられましたフラスコSQLAlchemyのために輸入していました。混乱を招いて申し訳ありません。

+0

db.session.remove'が不要である 'を呼び出して、実際に問題を引き起こす可能性がティアダウン機能で設定されているよう@app.teardown_appcontextは必要ありません。 – davidism

+1

teardown関数は、Flaskのドキュメントに従って、Declarativeメソッドを使用してnessecaryです。http://flask.pocoo.org/docs/0.10/patterns/sqlalchemy/ –

+1

これはスコープ付きセッションなので、それは冗長です。しかし、それを指摘してくれてありがとう、私はそれらのドキュメントを修正する必要があります。 – davidism

答えて

12

Flask-SQLAlchemy拡張は、ほとんどのFlask拡張と同様に、工場外で作成し、init_appを使用して工場で初期化する必要があります。これは、アプリケーションが作成される前にdbオブジェクトを使用できるようにするためです。

db = SQLAlchemy() 

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

あなたのFlaskアプリケーションは、適切に設計されたPythonプロジェクトと同様に、インストール可能なパッケージである必要があります。これは簡単です:プロジェクトのレイアウトが正しいことを確認してから、基本的なsetup.pyファイルを追加します。

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 

今、あなたはそれが他のプロジェクトで使用するために、データベースだと一緒に、あなたのフラスコのアプリをインストールすることができます。インストールし、それを2番目のプロジェクトのvirtualenvにインポートしてから、アプリケーションを作成してプッシュして初期化します。この方向に思い切って他の人のために

$ 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(...) 
+2

@davidismありがとうございますが、これは私のセカンダリプロジェクト内に完全なフラスコのコンテキストを作成します。セカンダリプロジェクトは、実行可能なバックグラウンドプロセスです。同じデータベースモデルを共有するだけの完全なフラスコのインスタンス/コンテキストを実行することは少し残酷すぎるようです。 Flaskの下で実行することなく、モデルだけを分離したいと思います。 –

+1

私は同意しません。 Flaskのコンテキストはそれほど複雑ではなく、正確に1回処理します。あなたは実際にフラスコを使用していない、あなたはその構成を使っているだけです。私はセロリで、私のアプリのコンテキストのすべてのバックグラウンドタスクを実行し、問題は一度もない。また、タスクが完了した後にメールを送信するようなことをしたい場合はどうしたらよいでしょうか?または、Bcryptを使用してパスワードを生成しますか?ホイールが既に定義されて構成されているときにホイールを再発明し続けるのは愚かでしょう。 – davidism

+2

hhhmm ..私はあなたの意見を見ますが、もし私が*電子メールを送信したりBcryptを使う必要がなければ(もし可能性が高い)、物語のもう片方はどうですか?その場合、私のコードベースには決して使用されないか更新されたコードがたくさんあります。または、FlaskのBcryptライブラリの新しいバージョンが必要な場合、私のセカンダリプロジェクトと互換性がありません。私はむしろ、これらの状況を防ぐために必要なものを使用/インポートする別々のプロジェクトを持っています。 –

2

There is quite a good blog postlink to a libraryは、SQLAlchemyをFlaskに直接リンクすることなく、Flask-SQLAlchemyの利点を提供します。

しかし、警告の言葉;私はAlchyを使用しようとしましたが、それをFlaskと非Webアプリケーションの両方に統合する方法をまだ分かりませんでしたので、私はこの質問にダビデズムによる承認された回答を行いました。あなたのマイレージは異なる場合があります。

+1

@dgillandはAlchyをFlaskと非Webアプリケーションの両方と統合するための答えを提供することができます –

1

同じ問題が発生しました。

"SQLALCHEMY_ECHO"を有効にすると、新しいトランザクションが開始されたにもかかわらず、対応するCOMMIT/ROLLBACKが見つからないことがあります。

私が知ったところでは、モデルファイルとweb.pyで作成した2つのSQLAlchemyインスタンスと何か関係があります。あなたがあなたのweb.pyのセッションと対話し、あなたのモデルに問い合わせると、COMMITを受け取るコンテキストがいくつか切り替わります。

モデルから "db"をインポートし、db.init_app(app)を呼び出してinitを呼び出すことで問題を解決しました。ログによれば、コミットは現在findeで動作します。

それはフラスコSQLAlchemyののSQLAlchemyのクラス(https://github.com/mitsuhiko/flask-sqlalchemy/blob/master/flask_sqlalchemy/init.py

関連する問題