Подтвердить что ты не робот

Использование Flask-SQLAlchemy в моделях Blueprint без ссылки на приложение

Я пытаюсь создать "модульное приложение" в Flask с помощью Blueprints.

При создании моделей, однако, я сталкиваюсь с проблемой обращения к приложению, чтобы получить db -объект, предоставленный Flask-SQLAlchemy. Я хотел бы иметь возможность использовать некоторые чертежи с несколькими приложениями (аналогично тому, как приложения Django могут быть использованы), поэтому это нехорошее решение. *

  • Можно сделать switcharoo и создать Blueprint для создания экземпляра db, который приложение затем импортирует вместе с остальной частью чертежа. Но тогда любой другой проект, желающий создавать модели, должен импортировать из этого проекта вместо приложения.

Мои вопросы таковы:

  • Есть ли способ, чтобы Blueprints определял модели без какой-либо осведомленности о приложении, которое они используют в дальнейшем, - и есть ли несколько чертежей вместе? Под этим я подразумеваю необходимость импортировать модуль/пакет приложения из вашего Blueprint.
  • Неужели я ошибаюсь с самого начала? Являются ли чертежи не независимыми от приложения и не распространяются (приложения la Django)?
    • Если нет, то какой шаблон следует использовать для создания чего-то подобного? Расширения фляг? Если вы просто этого не сделаете - и, возможно, централизовать все модели/схемы à la Ruby on Rails?

Изменить: я уже думал об этом сам, и это может быть больше связано с SQLAlchemy, чем с Flask, потому что при объявлении моделей нужно иметь declarative_base(). И это должно произойти откуда-то, во всяком случае!

Возможно, лучшим решением является определение схемы проекта в одном месте и распространение ее, как это делает Ruby on Rails. Декларативные определения класса SQLAlchemy действительно больше похожи на schema.rb, чем Django models.py. Я предполагаю, что это также облегчило бы использование миграций (от alembic или sqlalchemy-migrate).


Мне было предложено привести пример, поэтому давайте сделаем что-то простое: скажем, у меня есть план, описывающий "flatpages" - простой, "статический" контент, хранящийся в базе данных. Он использует таблицу с простым именем (для 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

Этот вопрос связан с:

И другие, но все ответы, похоже, полагаются на импорт экземпляра app db или на обратное. Страница "Большое приложение, как" вики-страницы также использует шаблон "импортировать ваше приложение в ваш план".

* Поскольку официальная документация показывает, как создавать маршруты, представления, шаблоны и активы в Blueprint, не заботясь о том, какое приложение оно "включено", я предположил, что Blueprints должны, в общем, быть повторно используемыми в приложениях, Однако эта модульность не кажется полезной без наличия независимых моделей.

Так как Blueprints можно подключить к приложению более одного раза, может быть, это неправильный подход к модели в "Чертежах"?

4b9b3361

Ответ 1

Я считаю, что самый верный ответ заключается в том, что модульные чертежи не должны напрямую касаться доступа к данным, а вместо этого полагаться на приложение, обеспечивающее совместимую реализацию.

Итак, учитывая ваш примерный проект.

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 и вместо этого просто использовать vanial declarative_base из sqlalchemy, но это должно представлять собой суть этого.

Ответ 2

Чертежи - это скорее логическое разделение проблем, чем фактические приложения.

Экземпляр базы данных должен быть настроен приложением, а не Blueprint, чтобы в одном месте можно было настроить такие параметры, как обработка фона и другие модули. Если Blueprint настроит базу данных, она будет зависеть от остальной части приложения Flask, сделав ее не такой модульной.

Взгляните на https://github.com/masom/Bluemonk для приложения с флагом среднего размера, используя множество различных плагинов Flask и шаблонов проектирования.

Ответ 3

Вы спросили: "Являются ли чертежи не независимыми от приложения и могут быть распространены (приложения la Django)?"

Ответ: да. Чертежи не похожи на приложение Django.

Если вы хотите использовать разные приложения/конфигурации, вам необходимо использовать "Диспетчер приложений", а не чертежи. Прочитай это [1]: http://flask.pocoo.org/docs/patterns/appdispatch/#app-dispatch [1]

Кроме того, ссылка здесь [1] http://flask.pocoo.org/docs/blueprints/#the-concept-of-blueprints [1]

В нем четко сказано, и я цитирую: "План в Flask не является подключаемым приложением, потому что на самом деле это не приложение - его набор операций, который может быть зарегистрирован в приложении, даже несколько раз. Почему бы не иметь несколько объектов приложения Вы можете сделать это (см. "Диспетчер приложений" ), но ваши приложения будут иметь отдельные конфигурации и будут управляться на уровне WSGI. "

Ответ 4

У меня есть аналогичные потребности в создании чертежей полностью модульных и не имеющих ссылки на приложение. Я придумал, возможно, чистое решение, но я не уверен, насколько он прав и каковы его ограничения.

Идея состоит в создании отдельного объекта db (db = SQLAlchemy()) внутри чертежа и вызова методов init_app() и create_all(), из которых создается корневое приложение.

Вот пример кода, чтобы показать, как структурирован проект: Приложение называется jobs, а чертеж называется status, и он хранится внутри папки чертежей.

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

Я тестировал его с помощью gunicorn с gevent рабочим, и он работает. Я задал отдельный вопрос о надежности решения здесь: Создайте один экземпляр SQLAlchemy для каждого проекта и вызовите create_all несколько раз