Program Tip

앱을 참조하지 않고 Blueprint 모델에서 Flask-SQLAlchemy 사용

programtip 2020. 11. 25. 08:20
반응형

앱을 참조하지 않고 Blueprint 모델에서 Flask-SQLAlchemy 사용


블루 프린트를 사용하여 Flask에서 "모듈 형 애플리케이션"을 만들려고합니다.

그러나 모델을 만들 때 dbFlask-SQLAlchemy에서 제공 하는 -object 를 가져 오기 위해 앱을 참조해야하는 문제가 있습니다. 하나 이상의 앱 (Django 앱을 사용할 수있는 방법과 유사)에서 일부 청사진을 사용할 수 있기를 원하므로 좋은 솔루션이 아닙니다. *

  • switcharoo를 수행하고 Blueprint가 db인스턴스를 생성하도록 할 수 있습니다. 그러면 앱이 나머지 Blueprint와 함께 가져옵니다. 그러나 모델을 생성하려는 다른 블루 프린트 는 앱 대신 해당 블루 프린트 에서 가져와야 합니다.

따라서 내 질문은 다음과 같습니다.

  • 블루 프린트가 나중에 사용되는 앱에 대한 인식없이 모델을 정의하고 여러 블루 프린트가 함께 모이도록하는 방법이 있습니까? 즉, 블루 프린트에서 앱 모듈 / 패키지를 임포트해야합니다.
  • 처음부터 내가 틀렸나 요? Blueprint는 앱과 독립적이고 재배포 가능 (Django 앱)을 의미하지 않습니까?
    • 그렇지 않다면 그런 것을 만들기 위해 어떤 패턴을 사용해야 합니까? 플라스크 확장? 그렇게하지 말아야할까요? 모든 모델 / 스키마를 Ruby on Rails로 중앙 집중화해야합니까?

편집 : 나는 이것에 대해 지금 생각하고 있으며 declarative_base()모델을 선언 할 때 있어야하기 때문에 Flask보다 SQLAlchemy와 더 관련이있을 수 있습니다 . 그리고 의는 것을 가지고 어쨌든, 어디에서 와서!

아마도 가장 좋은 해결책은 Ruby on Rails처럼 프로젝트의 스키마를 한곳에 정의하고 분산시키는 것입니다. 선언적 SQLAlchemy 클래스 정의는 Django의 models.py보다 실제로 schema.rb와 비슷합니다. 이것은 또한 마이그레이션 ( alembic 또는 sqlalchemy-migrate에서 ) 을 사용하기 쉽게 만들 것이라고 생각합니다 .


예제를 제공하라는 요청을 받았으므로 간단한 작업을 수행해 보겠습니다. 데이터베이스에 저장된 단순하고 "정적"콘텐츠 인 "플랫 페이지"를 설명하는 청사진이 있다고 가정 해 보겠습니다. 짧은 이름 (URL 용), 제목 및 본문 만있는 테이블을 사용합니다. 이것은 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)

그런 다음이 블루 프린트가 자체 모델을 정의하도록하는 것이 좋습니다 ( 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

이 질문은 다음과 관련이 있습니다.

그리고 다른 여러 가지이지만 모든 응답은 앱의 db인스턴스 를 가져 오거나 그 반대로 수행하는 것에 의존하는 것 같습니다 . "어떻게 대형 응용 프로그램" 위키 페이지에는 "당신의 청사진에 응용 프로그램을 가져"패턴을 사용합니다.

* 공식 문서는 어떤 앱이 "속"되어 있는지 신경 쓰지 않고 블루 프린트에서 경로, 뷰, 템플릿 및 애셋을 생성하는 방법을 보여주기 때문에, 일반적으로 블루 프린트는 여러 앱에서 재사용 할 수 있어야한다고 가정했습니다. 그러나,이 모듈 방식은하지 않는 것 독립적 모델을하지 않고 유용합니다.

블루 프린트를 앱에 두 번 이상 연결할 수 있기 때문에 블루 프린트에 모델을 갖는 것이 잘못된 접근 방식 일 수 있습니까?


가장 정답은 모듈 식 청사진이 데이터 액세스와 직접적으로 관련되어서는 안되며 대신 호환 가능한 구현을 제공하는 애플리케이션에 의존해야한다는 것입니다.

예를 들어 청사진이 주어졌습니다.

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)

이로부터 기본 구현을 제공하는 데 방해가되는 것은 없습니다.

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

그리고 귀하의 구성.

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

위의 내용은 db.Model의 직접 상속에 의존하지 않고 대신 sqlalchemy의 바닐라 declarative_base를 사용하여 더 깔끔하게 만들 수 있지만 이것이 그 요지를 나타내야합니다.


Blueprints를 완전히 모듈화하고 앱에 대한 참조가없는 유사한 요구 사항이 있습니다. 나는 아마도 깨끗한 해결책을 생각해 냈지만 그것이 얼마나 정확하고 그 한계가 무엇인지 잘 모르겠습니다.

The idea is to create a separate db object (db = SQLAlchemy()) inside the blueprint and call the init_app() and create_all() methods from where the root app is created.

Here's some sample code to show how the project is structured: The app is called jobs and the blueprint is called status and it is stored inside the blueprints folder.

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

I tested it with gunicorn with gevent worker and it works. I asked a separate question about the robustness of the solution here: Create one SQLAlchemy instance per blueprint and call create_all multiple times


You asked "Are Blueprints not meant to be independent of the app and be redistributable (à la Django apps)? "

The answer is yes. Blueprints are not similar to Django App.

If you want to use different app/configurations, then you need to use "Application Dispatching" and not blueprints. Read this [1]: http://flask.pocoo.org/docs/patterns/appdispatch/#app-dispatch [1]

Also, the link here [1] http://flask.pocoo.org/docs/blueprints/#the-concept-of-blueprints [1]

It clearly says and I quote "A blueprint in Flask is not a pluggable app because it is not actually an application – it’s a set of operations which can be registered on an application, even multiple times. Why not have multiple application objects? You can do that (see Application Dispatching), but your applications will have separate configs and will be managed at the WSGI layer."

참고URL : https://stackoverflow.com/questions/13058800/using-flask-sqlalchemy-in-blueprint-models-without-reference-to-the-app

반응형