将“外部"类模型与 Flask sqlalchemy 相关联 [英] Associate "external' class model with flask sqlalchemy

查看:31
本文介绍了将“外部"类模型与 Flask sqlalchemy 相关联的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们为各种 Python 模块使用了一个中心类模型.该模型是使用 SQLAlchemy 定义的.这些类都继承自 declarative_base.

We use a central class model for a wide variety of python modules. This model is defined using SQLAlchemy. The classes all inherit from declarative_base.

例如,我们的模型定义如下所示:

For example, our model definitions look something like this:

Base = declarative_base()

class Post(Base):
    __tablename__ = 'Posts'
    id = Column(INT, primary_key=True, autoincrement=True)
    body = Column(TEXT)
    timestamp = Column(TIMESTAMP)
    user_id = Column(INT, ForeignKey('Users.uid'))

我们一直在构建一个 Flask 网络应用程序,我们在其中使用了相同的模型.我们发现了一个棘手的问题,即 Flask-sqlalchemy 的设计方式似乎是它期望在其模型中使用的所有类都是通过传入会话的活动实例来定义的.这是一个正确的"flask-sqalchemy 类模型定义的示例:

We have been building a flask web-application in which we employ this same model. We have discovered a tricky problem in that flask-sqlalchemy appears to be designed in such a way that it expects all classes used in its model to have been defined by passing in an active instance of the session. Here is an example of a "proper" flask-sqalchemy class model definition:

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


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

请注意,上面的flask-sqlalchemy 示例需要一个已经实例化的sql 会话.这让我们感到震惊,因为我们完全不知道如何将我们的 SqlAlchemy 模型集成到烧瓶中.我们特别想使用flask-security 套件.

Note that the above example for flask-sqlalchemy requires an already-instantiated sql session. This has horrified us, because we are completely at a loss as to how to integrate our SqlAlchemy model into flask. We really want to use the flask-security suite in particular.

这个问题之前已经在SO上提出过.在这里,例如:如何将flask-sqlalchemy与现有的sqlalchemy模型一起使用?

This problem has been brought up before on SO. Here, for example: How to use flask-sqlalchemy with existing sqlalchemy model?

我们的要求与那里接受回复的人的要求不同.回复指出失去了使用User.query的能力,但这恰恰是我们必须保留的东西之一.

Our requirements are different from those of whoever accepted the response there. The response points out that one loses the ability to use User.query, but this is precisely one of the things we must retain.

放弃我们漂亮的、优雅的、中心的类模型定义来支持flask-sqlalchemy 似乎需要的东西是不可行的.有什么方法可以让我们将模型与 SQLAlchemy() 对象相关联吗?在我们的类上获取 .query() 方法的奖励积分,这似乎是flask-security 所必需的.

It is not feasible to abandon our nice, elegant, central class model definition in favor of what flask-sqlalchemy appears to require. Is there any way for us to associate our model with the SQLAlchemy() object? Bonus points for getting us the .query() method on our classes which appears to be required by flask-security.

推荐答案

解决方案:

截至今天,执行此操作的最佳方法如下:

As of today, the best way to do this is as follows:

实现或导入 sqlalchemy base

from sqlalchemy.ext.declarative import declarative_base

base = declarative_base()


class Base(base):

    __abstract__ = True
    uid = Column(Integer, primary_key=True, autoincrement=True)

注册外部基地:

from flask_sqlalchemy import SQLAlchemy
from model.base import Base

app = Flask(__name__)
db = SQLAlchemy(app, model_class=Base)

为后代存档:

我花了很多时间寻找这个问题的答案.这在今天比我最初提出这个问题时要容易得多,但仍然不那么简单.

Archived for posterity:

I spent a lot of time looking for an answer to this. This is a lot easier to do today than it was when I originally asked the question, but it still isn't exactly simple.

对于决定自己做安全性的任何人,我推荐以下对使用flask的常见设计模式的精彩阐述,但避免使用不必要的依赖项,如flask-security:https://exploreflask.com/users.html

For anyone who decides to do security themselves, I recommend the following excellent exposition of common design patterns which use flask, but which avoid employing unnecessary dependencies like flask-security: https://exploreflask.com/users.html

更新:对于任何感兴趣的人,与此相关的补丁已经开发了一段时间.截至目前,它还没有发布,但您可以在此处查看其进度:https://github.com/mitsuhiko/flask-sqlalchemy/pull/250#issuecomment-77504337

UPDATE: For anyone interested, a patch has been in the works for some time related to this. As of now it still isn't released, but you can check its progress here: https://github.com/mitsuhiko/flask-sqlalchemy/pull/250#issuecomment-77504337

更新:我从上面提到的补丁中获取了代码,并为 SQLAlchemy 对象创建了一个本地覆盖,它允许注册一个外部基础.我认为这是最好的选择,直到 FSA 开始正式添加它.这是该课程的代码,供感兴趣的任何人使用.测试与 Flask-SqlAlchemy 2.2 一起使用

UPDATE: I have taken the code from the above mentioned patch and created a local override for the SQLAlchemy object which allows one to register an external base. I think this is the best option available until such time as FSA gets around to adding this officially. Here is the code from that class for anyone interested. Tested working with Flask-SqlAlchemy 2.2

在 register_external_base 中修补:

import flask_sqlalchemy
'''Created by Isaac Martin 2017. Licensed insofar as it can be according to the standard terms of the MIT license: https://en.wikipedia.org/wiki/MIT_License. The author accepts no liability for consequences resulting from the use of this software. '''
class SQLAlchemy(flask_sqlalchemy.SQLAlchemy):
    def __init__(self, app=None, use_native_unicode=True, session_options=None,
                 metadata=None, query_class=flask_sqlalchemy.BaseQuery, model_class=flask_sqlalchemy.Model):

        self.use_native_unicode = use_native_unicode
        self.Query = query_class
        self.session = self.create_scoped_session(session_options)
        self.Model = self.make_declarative_base(model_class, metadata)
        self._engine_lock = flask_sqlalchemy.Lock()
        self.app = app
        flask_sqlalchemy._include_sqlalchemy(self, query_class)
        self.external_bases = []

        if app is not None:
            self.init_app(app)

    def get_tables_for_bind(self, bind=None):
        """Returns a list of all tables relevant for a bind."""
        result = []
        for Base in self.bases:
            for table in flask_sqlalchemy.itervalues(Base.metadata.tables):
                if table.info.get('bind_key') == bind:
                    result.append(table)

        return result

    def get_binds(self, app=None):
        """Returns a dictionary with a table->engine mapping.
        This is suitable for use of sessionmaker(binds=db.get_binds(app)).
        """
        app = self.get_app(app)
        binds = [None] + list(app.config.get('SQLALCHEMY_BINDS') or ())
        retval = {}
        for bind in binds:
            engine = self.get_engine(app, bind)
            tables = self.get_tables_for_bind(bind)
            retval.update(dict((table, engine) for table in tables))
        return retval

    @property
    def bases(self):
        return [self.Model] + self.external_bases

    def register_base(self, Base):
        """Register an external raw SQLAlchemy declarative base.
        Allows usage of the base with our session management and
        adds convenience query property using self.Query by default."""

        self.external_bases.append(Base)
        for c in Base._decl_class_registry.values():
            if isinstance(c, type):
                if not hasattr(c, 'query') and not hasattr(c, 'query_class'):
                    c.query_class = self.Query
                if not hasattr(c, 'query'):
                    c.query = flask_sqlalchemy._QueryProperty(self)

                    # for name in dir(c):
                    #     attr = getattr(c, name)
                    #     if type(attr) == orm.attributes.InstrumentedAttribute:
                    #         if hasattr(attr.prop, 'query_class'):
                    #             attr.prop.query_class = self.Query

                    # if hasattr(c , 'rel_dynamic'):
                    #     c.rel_dynamic.prop.query_class = self.Query

像这样使用:

app = Flask(__name__)
db = SQLAlchemy(app)
db.register_base(base)

这篇关于将“外部"类模型与 Flask sqlalchemy 相关联的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆