web-dev-qa-db-fra.com

Problème d'importation / de contexte Flask-SQLAlchemy

Je veux structurer mon Flask app quelque chose comme:

./site.py
./apps/members/__init__.py
./apps/members/models.py

apps.members est un Flask Blueprint.

Maintenant, afin de créer les classes de modèle, j'ai besoin de disposer de l'application, par exemple:

# apps.members.models
from flask import current_app
from flaskext.sqlalchemy import SQLAlchemy

db = SQLAlchemy(current_app)

class Member(db.Model):
    # fields here
    pass

Mais si j'essaie d'importer ce modèle dans mon application Blueprint, j'obtiens le redouté RuntimeError: working outside of request context. Comment puis-je récupérer correctement mon application ici? Les importations relatives peuvent fonctionner, mais elles sont plutôt laides et ont leurs propres problèmes de contexte, par exemple:

from ...site import app

# ValueError: Attempted relative import beyond toplevel package
102
Brad Wright

Le module flask_sqlalchemy Ne doit pas nécessairement être initialisé avec l'application: vous pouvez le faire à la place:

# apps.members.models
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

class Member(db.Model):
    # fields here
    pass

Ensuite, dans la configuration de votre application, vous pouvez appeler init_app:

# apps.application.py
from flask import Flask
from apps.members.models import db

app = Flask(__name__)
# later on
db.init_app(app)

De cette façon, vous pouvez éviter les importations cycliques.

Ce motif ne nécessite pas la place de tous vos modèles dans un seul fichier. Importez simplement la variable db dans chacun de vos modules de modèle.

Exemple

# apps.shared.models
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()
# apps.members.models
from apps.shared.models import db

class Member(db.Model):
    # TODO: Implement this.
    pass
# apps.reporting.members
from flask import render_template
from apps.members.models import Member

def report_on_members():
    # TODO: Actually use arguments
    members = Member.filter(1==1).all()
    return render_template("report.html", members=members)
# apps.reporting.routes
from flask import Blueprint
from apps.reporting.members import report_on_members

reporting = Blueprint("reporting", __name__)

reporting.route("/member-report", methods=["GET","POST"])(report_on_members)
# apps.application
from flask import Flask
from apps.shared import db
from apps.reporting.routes import reporting

app = Flask(__name__)
db.init_app(app)
app.register_blueprint(reporting)

Note: ceci est un esquisse de la puissance que cela vous donne - il y a évidemment beaucoup plus que ce que vous pouvez faire pour rendre le développement encore plus facile (en utilisant un motif create_app, en enregistrant automatiquement des plans dans certains dossiers, etc.)

247
Sean Vieira

un original app.py : http://pythonhosted.org/Flask-SQLAlchemy/quickstart.html#quickstart

...

app = flask.Flask(__name__)
app.config['DEBUG'] = True
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = flask.ext.sqlalchemy.SQLAlchemy(app)

class Person(db.Model):
    id = db.Column(db.Integer, primary_key=True)
...

class Computer(db.Model):
    id = db.Column(db.Integer, primary_key=True)
...

# Create the database tables.
db.create_all()

...

# start the flask loop
app.run()

Je viens de scinder une application.py en app.py et model.py sans utiliser Blueprint. Dans ce cas, la réponse ci-dessus ne fonctionne pas. Un code de ligne est nécessaire pour fonctionner.

avant :

db.init_app(app)

après :

db.app = app
db.init_app(app)

Et le lien suivant est très utile.

http://piotr.banaszkiewicz.org/blog/2012/06/29/flask-sqlalchemy-init_app/

22
cybaek