Utiliser Flask-SQLAlchemy dans les Modèles de plan directeur sans référence à l'application

j'essaie de créer une" application modulaire " en Flasque en utilisant des Bleus.

lors de la création de modèles, cependant, je me heurte au problème d'avoir à faire référence à l'application afin d'obtenir le db -objet fourni par Flask-SQLAlchemy. J'aimerais pouvoir utiliser quelques plans avec plus d'une application (similaire à la façon dont les applications Django peuvent être utilisées), donc ce n'est pas une bonne solution.*

  • il est possible de faire un switcharoo, et demandez au Blueprint de créer l'instance db , que l'application importe ensuite avec le reste du blueprint. Mais alors, tout autre modèle souhaitant créer des modèles doivent importer de que plan au lieu de l'application.

mes questions sont ainsi:

  • est-il possible de laisser Blueprints définir des modèles sans aucune conscience de l'application dans laquelle ils sont utilisés plus tard -- et avoir plusieurs Les bleus? Par cela, je veux dire devoir importer le module/paquet app de votre plan directeur.
  • ai-je tort dès le début? Les Blueprints ne sont-ils pas censés être indépendants de l'application et être redistribuables (à la Django apps)?
    • si non, alors quel modèle devrait vous utilisez pour créer quelque chose comme ça? Flacon extensions? Ne devriez-vous simplement pas le faire -- et peut-être centraliser tous les modèles/schémas à la Ruby sur Rails?

Edit : j'ai pensé à cela moi-même maintenant, et cela pourrait être plus lié à SQLAlchemy que Flask parce que vous devez avoir le declarative_base() lors de la déclaration des modèles. Et c'est doit venir de quelque part, de toute façon!

peut-être la meilleure solution est d'avoir le schéma de votre projet défini dans un endroit et de le diffuser autour, comme Ruby on Rails. Déclarative SQLAlchemy définitions de classe sont vraiment plus comme schéma.rb than Django's models.py. J'imagine que cela faciliterait aussi l'utilisation des migrations (de alambic ou sqlalchemy-migrate ).


on m'a demandé de fournir un exemple, alors faisons quelque chose de simple: disons que j'ai un plan décrivant des "flatpages" -- un contenu simple, "statique" stocké dans le la base de données. Il utilise une table avec juste shortname (pour les URLs), un titre et un corps. C'est simple_pages/__init__.py :

from flask import Blueprint, render_template
from .models import Page

flat_pages = Blueprint('flat_pages', __name__, template_folder='templates')

@flat_pages.route('/<page>')
def show(page):
    page_object = Page.query.filter_by(name=page).first()
    return render_template('pages/{}.html'.format(page), page=page_object)

alors, il serait bien de laisser ce schéma définir son propre modèle (ce dans simple_page/models.py ):

# TODO Somehow get ahold of a `db` instance without referencing the app
# I might get used in!

class Page(db.Model):
    name = db.Column(db.String(255), primary_key=True)
    title = db.Column(db.String(255))
    content = db.Column(db.String(255))

    def __init__(self, name, title, content):
        self.name = name
        self.title = title
        self.content = content

cette question est liée à:

  • en Flacon de SQLAlchemy import/le contexte de l'émission
  • Quelle est votre mise en page d'un dossier pour une application Flask divisée en modules?

et divers autres, mais toutes les réponses semblent reposer sur l'importation de l'application db instance, ou faire l'inverse. Le "Grand app comment" page wiki utilise également le "importation de votre application dans votre plan".

* depuis la documentation officielle montre comment créer des itinéraires, des vues, des gabarits et des actifs dans un schéma sans en me souciant de l'application dans laquelle il est "in", j'ai supposé que les bleus devraient, en général, être réutilisables à travers les applications. Cependant, cette modularité ne semble pas que utile sans avoir aussi des modèles indépendants.

étant donné que les bleus peuvent être connectés à une application plus d'une fois, il se peut que ce soit simplement la mauvaise approche d'avoir des modèles dans les Bleus?

58
demandé sur Community 2012-10-25 02:28:25

3 réponses

je crois que la réponse la plus vraie est que les plans modulaires ne devraient pas se préoccuper directement de l'accès aux données, mais plutôt compter sur l'application fournissant une mise en œuvre compatible.

donc vu votre exemple de plan.

from flask import current_app, Blueprint, render_template

flat_pages = Blueprint('flat_pages', __name__, template_folder='templates')

@flat_pages.record
def record(state):
    db = state.app.config.get("flat_pages.db")

    if db is None:
        raise Exception("This blueprint expects you to provide "
                        "database access through flat_pages.db")

@flat_pages.route('/<page>')
def show(page):
    db = current_app.config["flat_pages.db"]
    page_object = db.find_page_by_name(page)
    return render_template('pages/{}.html'.format(page), page=page_object)

rien ne vous empêche de fournir une implémentation par défaut.

def setup_default_flat_pages_db(db):
    class Page(db.Model):
        name = db.Column(db.String(255), primary_key=True)
        title = db.Column(db.String(255))
        content = db.Column(db.String(255))

        def __init__(self, name, title, content):
            self.name = name
            self.title = title
            self.content = content

    class FlatPagesDBO(object):
        def find_page_by_name(self, name):
            return Page.query.filter_by(name=name).first()

    return FlatPagesDBO()

et dans votre configuration.

app.config["flat_pages.db"] = setup_default_flat_pages_db(db)

Ce qui précède pourrait être rendu plus propre en ne comptant pas dans l'héritage direct de db.Modèle et à la place il suffit d'utiliser une base déclarative_de SQLAlchemy, mais cela devrait représenter l'essentiel de celui-ci.

33
répondu udoprog 2012-11-17 16:22:06

j'ai des besoins similaires de faire des Bleus complètement modulaire et n'ayant aucune référence à L'application. J'ai trouvé une solution peut-être propre, mais je ne sais pas si elle est correcte et quelles sont ses limites.

l'idée est de créer un objet séparé db ( db = SQLAlchemy() ) à l'intérieur du schéma et d'appeler les méthodes init_app() et create_all() d'où l'application racine est créée.

voici un exemple de code pour montrer comment le projet est structuré: L'application est appelée jobs et le plan est appelé status et il est stocké à l'intérieur du dossier des plans.

blueprints.status.models.py

from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()  # <--- The db object belonging to the blueprint

class Status(db.Model):
    __tablename__ = 'status'
    id = db.Column(db.Integer, primary_key=True)
    job_id = db.Column(db.Integer)
    status = db.Column(db.String(120))

models.py

from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()  # <--- The db object belonging to the root app

class Job(db.Model):
    __tablename__ = 'job'
    id = db.Column(db.Integer, primary_key=True)
    state = db.Column(db.String(120)

factory.py

from .blueprints.status.models import db as status_db  # blueprint db
from .blueprints.status.routes import status_handler   # blueprint handler
from .models import db as root_db                      # root db
from flask import Flask

def create_app():
    app = Flask(__name__)

    # Create database resources.
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////path/to/app.db'
    root_db.init_app(app)
    status_db.init_app(app)     # <--- Init blueprint db object.
    with app.app_context():
        root_db.create_all()
        status_db.create_all()  # <--- Create blueprint db.

    # Register blueprint routes.
    app.register_blueprint(status_handler, url_prefix="/status")

    return app

je l'ai testé avec gunicorn avec gevent travailleur et il fonctionne. J'ai posé une question séparée sur la robustesse de la solution ici.: créer une instance SQLAlchemy par blueprint et CALL create_all plusieurs fois

2
répondu nitred 2017-12-25 10:54:58

avez-vous demandé "les Blueprints ne sont-ils pas censés être indépendants de l'application et être redistribuables (à la Django apps)? "

la réponse est oui. Les plans ne sont pas similaires à L'application Django.

si vous voulez utiliser différentes applications/configurations, alors vous devez utiliser" Application Dispatching " et non blueprints. Lire ce [1]: http://flask.pocoo.org/docs/patterns/appdispatch/#app-dispatch [1]

Also, le lien ici [1] http://flask.pocoo.org/docs/blueprints/#the-concept-of-blueprints [1]

il est clairement dit et je cite" un plan dans un flacon n'est pas une application connectable parce qu'il ne s'agit pas réellement d'une application – c'est un ensemble d'opérations qui peuvent être enregistrées sur une application, même plusieurs fois. Pourquoi ne pas avoir plusieurs objets d'application? Vous pouvez le faire (voir envoi D'applications), mais vos applications auront des configs séparées et seront géré par la couche WSGI."

0
répondu codegeek 2012-10-25 20:37:54