web-dev-qa-db-fra.com

Importation circulaire de référence db à l'aide de Flask-SQLAlchemy et Blueprints

J'utilise Flask-SQLAlchemy et Blueprints et je ne peux pas m'empêcher d'utiliser des importations circulaires. Je sais que je peux écrire des importations à l'intérieur des fonctions et le faire fonctionner mais cela semble désagréable, je voudrais confirmer avec la communauté s'il existe une meilleure façon de le faire.

Le problème est que j'ai un module (blueprints.py) où je déclare la base de données et importe les plans mais ces plans doivent importer la déclaration de base de données en même temps.

Voici le code (extrait des parties importantes):

application.apps.people.views.py

from application.blueprints import db

people = Blueprint('people', __name__,
                 template_folder='templates',
                 static_folder='static')

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True)

@people.route('/all')
def all():
    users = User.query.all()

application.blueprints.py

from application.apps.people.views import people

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app)
app.register_blueprint(people, url_prefix='/people')

J'ai lu la documentation et les questions que j'ai trouvées sur ce sujet, mais je ne trouve toujours pas la réponse que je cherche. J'ai trouvé ce chapitre ( https://pythonhosted.org/Flask-SQLAlchemy/contexts.html ) où il suggère de mettre le code d'initialisation dans une méthode mais l'importation circulaire persiste toujours.

Modifier J'ai résolu le problème en utilisant le modèle Application Factory

35
S182

J'ai résolu le problème à l'aide du modèle Application Factory . Je déclare la base de données dans un troisième module et la configure plus tard dans le même module dans lequel je démarre l'application.

Il en résulte les importations suivantes:

  • database.py → app.py
  • views.py → app.py
  • database.py → views.py

Il n'y a pas d'importation circulaire. Il est important de s'assurer que l'application a été démarrée et configurée avant d'appeler des opérations de base de données.

Voici un exemple d'application:

app.py

from database import db
from flask import Flask
import os.path
from views import User
from views import people


def create_app():
    app = Flask(__name__)
    app.config['DEBUG'] = True
    app.config['SQLALCHEMY_DATABASE_URI'] = "sqlite:////tmp/test.db"
    db.init_app(app)    
    app.register_blueprint(people, url_prefix='')
    return app 


def setup_database(app):
    with app.app_context():
        db.create_all()
    user = User()
    user.username = "Tom"
    db.session.add(user)
    db.session.commit()    


if __name__ == '__main__':
    app = create_app()
    # Because this is just a demonstration we set up the database like this.
    if not os.path.isfile('/tmp/test.db'):
      setup_database(app)
    app.run()

database.py

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

views.py

from database import db
from flask.blueprints import Blueprint


people = Blueprint('people', __name__,
                 template_folder='templates',
                 static_folder='static')


class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True)


@people.route('/')
def test():
  user = User.query.filter_by(username="Tom").first()
  return "Test: Username %s " % user.username
53
S182

Les importations circulaires dans Flask me rendent fou. D'après les documents: http://flask.pocoo.org/docs/0.10/patterns/packages/

... Sachez que c'est une mauvaise idée en général mais ici, c'est très bien.

Ce n'est pas bien. C'est profondément faux. J'envisage également de mettre n'importe quel code dans __init__.py comme une mauvaise pratique. Cela rend l'application plus difficile à mettre à l'échelle. Les plans directeurs sont un moyen d'atténuer le problème des importations circulaires. Je pense que Flask a besoin de plus de cela.

24
dexity

Je sais que cela a déjà été résolu, mais je l'ai résolu d'une manière légèrement différente et je voulais répondre au cas où cela aiderait les autres.

À l'origine, mon code d'application (par exemple my_app.py) avait cette ligne:

db = SQLAlchemy(app)

Et donc dans mon models.py, J'ai eu:

from my_app import db

class MyModel(db.Model):
    # etc

d'où les références circulaires lors de l'utilisation de MyModel dans my_app. J'ai mis à jour ceci pour que models.py ait ceci:

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()  # note no "app" here, and no import from my_app above

class MyModel(db.Model):
    # etc as before

puis dans my_app:

from models import db, MyModel  # importing db is new

# ...

db.init_app(app)  # call init_app here rather than initialising db here
5
Sam

Serge, sortez la définition des modèles dans un fichier séparé appelé models.py. Enregistrez le plan dans le fichier __init__.py Du package.

Vous avez une importation circulaire car un fichier de plan tente d'importer la référence des personnes à partir de views.py, Mais dans views.py, Vous essayez d'importer la base de données à partir de blueprints.py. Et tout cela se fait au niveau supérieur des modules.

Vous pouvez créer la structure de votre projet comme ceci:

app
  __init__.py  # registering of blueprints and db initialization
  mods
    __init__.py
    people
      __init__.py  # definition of module (blueprint)
      views.py  # from .models import User
      models.py # from app import db

PD:

Pour ceux qui sont dans le réservoir:

people/__init__.py -> mod = Module('app.mods.people', 'people')

people/views.py -> @mod.route('/page')

app/__init__.py -> from app.mods import people; from app.mods.people import views; app.register_blueprint(people.mod, **options);

3
Mike