すべてがタイトルにあります。 SQLAlchemyでSQLビューを定義するための「Python的な」方法(つまり、「純粋なSQL」クエリがない)はありますか?
ご協力いただきありがとうございます、
更新:SQLAlchemyの使用法のレシピも参照してください here
(読み取り専用の非実体化)ビューの作成は、私が知っている限り、すぐにサポートされていません。しかし、SQLAlchemy 0.7でこの機能を追加するのは簡単です(私が here に与えた例と同様です)。 コンパイラ拡張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)
)
ステファンの答えは良いものであり、ほとんどの基盤をカバーしていますが、不満のままになったのは、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()呼び出し中に自動的にドロップされます。
これは明らかに非常にハッキーなソリューションですが、私の目には、現時点で最高のものであり、最もクリーンなものです。私はここ数日それをテストしましたが、問題はありませんでした。リレーションシップを追加する方法がわかりません(そこで問題が発生しました)が、上記のクエリで示したように、実際には必要ありません。
誰かが入力をしたり、予期しない問題を見つけたり、物事を行うためのより良い方法を知っている場合は、コメントを残すか、私に知らせてください。
これはSQLAlchemy 1.2.6およびPython 3.6。でテストされました
最近では、そのためのPyPIパッケージがあります: SQLAlchemy Views 。
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
_
ただし、no "pure SQL"クエリを要求したため、おそらくdefinition
をSQLAlchemyクエリオブジェクトで作成する必要があります。 。
幸いなことに、上記の例のtext()
は、definition
の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
_
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
)
Base
はdeclarative_base
、sa
はSQLAlchemy
パッケージ、create_view
はsqlalchemy_utils.view
の関数です。
短くて便利な答えが見つかりませんでした。
Viewの追加機能(必要な場合)は必要ないので、単にビューを他のテーブル定義として通常のテーブルとして扱います。
したがって、基本的に、すべてのテーブルとビューを定義するa.py
、SQL関連のもの、main.py
からそれらのクラスをインポートして使用するa.py
があります。
以下がa.py
に追加して機能するものです。
class A_View_From_Your_DataBase(Base):
__table= 'View_Name'
keyword = Column(String(100), nullable=False, primary_key=True)
特に、ビューに主キーがない場合でも、primary_key
プロパティを追加する必要があります。