SQLAlchemyでSQLビューを作成するにはどうすればよいですか?


回答:


69

更新:ここでSQLAlchemyの使用レシピも参照してください

(読み取り専用の非マテリアライズド)ビューの作成は、私が知る限り、そのままではサポートされていません。しかし、SQLAlchemy 0.7にこの機能を追加するのは簡単です(ここで示した例と同様です)。コンパイラ拡張 を書く必要がありますCreateView。この拡張機能を使用すると、次のように記述できます(これtが列を持つテーブルオブジェクトであると想定id

createview = CreateView('viewname', t.select().where(t.c.id>5))
engine.execute(createview)

v = Table('viewname', metadata, autoload=True)
for r in engine.execute(v.select()):
    print r

これが実際の例です:

from sqlalchemy import Table
from sqlalchemy.ext.compiler import compiles
from sqlalchemy.sql.expression import Executable, ClauseElement

class CreateView(Executable, ClauseElement):
    def __init__(self, name, select):
        self.name = name
        self.select = select

@compiles(CreateView)
def visit_create_view(element, compiler, **kw):
    return "CREATE VIEW %s AS %s" % (
         element.name,
         compiler.process(element.select, literal_binds=True)
         )

# test data
from sqlalchemy import MetaData, Column, Integer
from sqlalchemy.engine import create_engine
engine = create_engine('sqlite://')
metadata = MetaData(engine)
t = Table('t',
          metadata,
          Column('id', Integer, primary_key=True),
          Column('number', Integer))
t.create()
engine.execute(t.insert().values(id=1, number=3))
engine.execute(t.insert().values(id=9, number=-3))

# create view
createview = CreateView('viewname', t.select().where(t.c.id>5))
engine.execute(createview)

# reflect view and print result
v = Table('viewname', metadata, autoload=True)
for r in engine.execute(v.select()):
    print r

必要に応じて、方言に特化することもできます。

@compiles(CreateView, 'sqlite')
def visit_create_view(element, compiler, **kw):
    return "CREATE VIEW IF NOT EXISTS %s AS %s" % (
         element.name,
         compiler.process(element.select, literal_binds=True)
         )

orm.mapperでmapvを使用できますか?v = Table('viewname', metadata, autoload=True) class ViewName(object): def __init__(self, name): self.name = name mapper(ViewName, v) 上記のように可能ですか?セッションでビューを使用するためです。
Syed Habib M

1
@SyedHabibM:はい、これは可能です。あなたは、かかわらず、手動で主キーを設定するようなもの持っていると、それぞれ自分の主キー(またはキー)とプロパティです。docs.sqlalchemy.org/en/latest/orm/…を参照してください。orm.mapper(ViewName, v, primary_key=pk, properties=prop)pkprop
ステファン2013

2
@SyedHabibM:あなたは何ができるステファンあなたが列指定をオーバーライドすることで、自動的にロードテーブルを使用する際にも言及し、PKを指定:v = Table('viewname', metadata, Column('my_id_column', Integer, primary_key=True), autoload=True)
バン

@SyedHabibMIは、実際の例を使用して、それぞれの質問stackoverflow.com/q/20518521/92092に回答しました。そこにヴァンのコメントも追加します。
ステファン2013

27

ステファンの答えは良いものであり、ほとんどのベースをカバーしていますが、私が不満を感じたのは、SQLAlchemyの他の部分(ORM、自動ドロップなど)との統合の欠如でした。インターネットの隅々からの知識を何時間も実験してつなぎ合わせた後、私は次のことを思いついた。

import sqlalchemy_views
from sqlalchemy import Table
from sqlalchemy.ext.compiler import compiles
from sqlalchemy.sql.ddl import DropTable


class View(Table):
    is_view = True


class CreateView(sqlalchemy_views.CreateView):
    def __init__(self, view):
        super().__init__(view.__view__, view.__definition__)


@compiles(DropTable, "postgresql")
def _compile_drop_table(element, compiler, **kwargs):
    if hasattr(element.element, 'is_view') and element.element.is_view:
        return compiler.visit_drop_view(element)

    # cascade seems necessary in case SQLA tries to drop 
    # the table a view depends on, before dropping the view
    return compiler.visit_drop_table(element) + ' CASCADE'

sqlalchemy_views物事を単純化するために、私はパッケージを利用していることに注意してください。

ビューの定義(たとえば、テーブルモデルのようにグローバルに):

from sqlalchemy import MetaData, text, Text, Column


class SampleView:
    __view__ = View(
        'sample_view', MetaData(),
        Column('bar', Text, primary_key=True),
    )

    __definition__ = text('''select 'foo' as bar''')

# keeping track of your defined views makes things easier
views = [SampleView]

ビューのマッピング(ORM機能を有効にする):

アプリをロードするとき、クエリの前、およびDBのセットアップ後に実行します。

for view in views:
    if not hasattr(view, '_sa_class_manager'):
        orm.mapper(view, view.__view__)

ビューの作成:

データベースを初期化するとき、たとえばcreate_all()呼び出しの後などに実行します。

from sqlalchemy import orm


for view in views:
    db.engine.execute(CreateView(view))

ビューをクエリする方法:

results = db.session.query(SomeModel, SampleView).join(
    SampleView,
    SomeModel.id == SampleView.some_model_id
).all()

これにより、期待どおりの結果が返されます(それぞれにSomeModelオブジェクトとSampleViewオブジェクトがあるオブジェクトのリスト)。

ビューの削除:

SampleView.__view__.drop(db.engine)

また、drop_all()呼び出し中に自動的にドロップされます。

これは明らかに非常にハッキーな解決策ですが、私の目には、現時点で最高の解決策であり、最もクリーンな解決策です。私はここ数日それをテストしました、そして、何の問題もありませんでした。関係を追加する方法がわかりません(そこで問題が発生しました)が、上記のクエリで示したように、実際には必要ありません。

誰かが何か意見を持っている、予期しない問題を見つけた、または物事を行うためのより良い方法を知っている場合は、コメントを残すか、私に知らせてください。

これはSQLAlchemy1.2.6とPython3.6でテストされました。


クレイジーなタイミング、これを自分で処理しているだけです。py2.7およびSQLa1.1.2(質問しないでください...)の場合、必要な変更は、super(CreateView, self).__init__およびclass SampleView(object)
Steven Dickinson

1
@Steven Dickinsonうん、そうですね!ええ、これは本当に一般的なタスクだと思いました。そのため、ドキュメントが非常に貧弱/時代遅れ/浅いことに驚きました。でもねえ、一度に一歩ずつだと思います。
fgblomqvist 2018

2
これを宣言的に行うことを検討している人のために、別のメタデータインスタンスを使用して、テーブルとは別のファイルでビューを定義 しました。元の投稿で提案されているように、プロパティをBase = declarative_base(metadata=db.MetaData()) class ViewSample(Base): __tablename__ = 'view_sample' 含め、__definition__CreateViewを呼び出して作成しました。最後に、ドロップデコレーションメソッドを変更する必要がありました if element.element.name.startswith('view_'): return compiler.visit_drop_view(element) 。埋め込みテーブルにプロパティを追加する方法が見つからなかったためです。
ケーシー

23

最近では、そのためのPyPIパッケージがあります:SQLAlchemyViews

PyPIページから:

>>> from sqlalchemy import Table, MetaData
>>> from sqlalchemy.sql import text
>>> from sqlalchemy_views import CreateView, DropView

>>> view = Table('my_view', metadata)
>>> definition = text("SELECT * FROM my_table")

>>> create_view = CreateView(view, definition, or_replace=True)
>>> print(str(create_view.compile()).strip())
CREATE OR REPLACE VIEW my_view AS SELECT * FROM my_table

ただし、「純粋なSQL」クエリを要求しなかったため、definitionSQLAlchemyクエリオブジェクトを使用して上記を作成することをお勧めします。

幸い、text()上記の例のは、definitiontoのパラメータCreateViewがそのようなクエリオブジェクトであることを明確にしています。したがって、次のようなものが機能するはずです。

>>> from sqlalchemy import Table, Column, Integer, String, MetaData, ForeignKey
>>> from sqlalchemy.sql import select
>>> from sqlalchemy_views import CreateView, DropView

>>> metadata = MetaData()

>>> users = Table('users', metadata,
...     Column('id', Integer, primary_key=True),
...     Column('name', String),
...     Column('fullname', String),
... )

>>> addresses = Table('addresses', metadata,
...   Column('id', Integer, primary_key=True),
...   Column('user_id', None, ForeignKey('users.id')),
...   Column('email_address', String, nullable=False)
...  )

ここに興味深いビットがあります:

>>> view = Table('my_view', metadata)
>>> definition = select([users, addresses]).where(
...     users.c.id == addresses.c.user_id
... )
>>> create_view = CreateView(view, definition, or_replace=True)
>>> print(str(create_view.compile()).strip())
CREATE OR REPLACE VIEW my_view AS SELECT users.id, users.name,
users.fullname, addresses.id, addresses.user_id, addresses.email_address 
FROM users, addresses 
WHERE users.id = addresses.user_id

17

SQLAlchemy-utilsは、0.33.6(pypiで利用可能)でこの機能追加しました。ビュー、マテリアライズドビューがあり、ORMと統合されています。まだ文書化されていませんが、ビュー+ ORMを正常に使用しています。

ORMを使用して、通常のビューとマテリアライズドビューの両方の例としてテストを使用できます

ビューを作成するには、パッケージをインストールしたら、上記のテストの次のコードをビューのベースとして使用します。

class ArticleView(Base):
    __table__ = create_view(
        name='article_view',
        selectable=sa.select(
            [
                Article.id,
                Article.name,
                User.id.label('author_id'),
                User.name.label('author_name')
            ],
            from_obj=(
                Article.__table__
                    .join(User, Article.author_id == User.id)
            )
        ),
        metadata=Base.metadata
    )

どこBasedeclarative_basesaあるSQLAlchemyパッケージ、およびcreate_viewからの機能ですsqlalchemy_utils.view


アランビックと一緒に使う方法を見つけましたか?
JorgeLeitao20年

1

短くて便利な答えが見つかりませんでした。

ビューの追加機能(ある場合)は必要ないので、ビューを他のテーブル定義と同じように通常のテーブルとして扱います。

つまり、基本的にa.py、すべてのテーブルとビュー、SQL関連のものを定義するmain.py場所と、それらのクラスをインポートしa.pyて使用する場所があります。

これが私が追加しa.pyて機能するものです:

class A_View_From_Your_DataBase(Base):
    __tablename__ = 'View_Name'
    keyword = Column(String(100), nullable=False, primary_key=True)

特に、primary_keyビューに主キーがない場合でも、プロパティを追加する必要があります。


-9

純粋なSQLなしのSQLビュー?クラスまたは関数を作成して、定義されたビューを実装できます。

function get_view(con):
  return Table.query.filter(Table.name==con.name).first()

2
申し訳ありませんが、それは私が尋ねたものではありません。私の英語は完璧ではありません。誤解してしまったらごめんなさい:)
Thibaut D.
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.