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

查看:96
本文介绍了将“外部"类模型与烧瓶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 Web应用程序,在该应用程序中,我们采用了相同的模型.我们发现了一个棘手的问题,因为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上提出过.例如,在这里: 如何在现有的sqlalchemy模型中使用flask-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()方法的好处.

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

Implement or import 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)

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

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