首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何用SQLAlchemy创建一个SQL视图?

如何用SQLAlchemy创建一个SQL视图?
EN

Stack Overflow用户
提问于 2012-03-19 16:22:51
回答 7查看 64.6K关注 0票数 92

有没有一种用SQLAlchemy定义SQL视图的"Pythonic“方式(我是说,没有”纯SQL“查询)?

EN

回答 7

Stack Overflow用户

回答已采纳

发布于 2012-03-19 19:34:32

更新:另请参阅SQLAlchemy usage recipe here

据我所知,创建(只读的、非物化的)视图不支持开箱即用。但在SQLAlchemy 0.7中添加此功能非常简单(类似于我给出的here示例)。您只需编写一个compiler extension CreateView即可。有了这个扩展,您就可以编写(假设t是一个具有列id的表对象)

代码语言:javascript
运行
复制
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

下面是一个有效的示例:

代码语言:javascript
运行
复制
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

如果你愿意,你也可以专门化一种方言,例如

代码语言:javascript
运行
复制
@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)
         )
票数 74
EN

Stack Overflow用户

发布于 2018-04-18 15:55:17

斯蒂芬的答案很好,涵盖了大多数基础,但让我不满意的是缺乏与SQLAlchemy其余部分的集成(对象关系映射、自动删除等)。经过几个小时的实验和拼凑来自互联网各个角落的知识后,我想出了以下几点:

代码语言:javascript
运行
复制
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包,只是为了简化操作。

定义视图的(例如,全局类似于您的表模型):

代码语言:javascript
运行
复制
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映射视图(启用功能):

当加载你的应用程序时,在任何查询之前和设置数据库之后。

代码语言:javascript
运行
复制
for view in views:
    if not hasattr(view, '_sa_class_manager'):
        orm.mapper(view, view.__view__)

创建视图的

初始化数据库时,例如在create_all()调用之后。

代码语言:javascript
运行
复制
from sqlalchemy import orm


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

如何查询视图:

代码语言:javascript
运行
复制
results = db.session.query(SomeModel, SampleView).join(
    SampleView,
    SomeModel.id == SampleView.some_model_id
).all()

这将返回您所期望的结果(一个对象列表,每个对象都有一个SomeModel对象和一个SampleView对象)。

删除视图:

代码语言:javascript
运行
复制
SampleView.__view__.drop(db.engine)

它还将在drop_all()调用期间自动删除。

这显然是一个非常老套的解决方案,但在我看来,这是目前最好、最干净的解决方案。这几天我已经测试过了,没有任何问题。我不确定如何添加关系(在那里遇到了问题),但这并不是真正必要的,就像上面的查询中演示的那样。

如果任何人有任何意见,发现任何意想不到的问题,或知道更好的方法来做事情,请留下评论或让我知道。

这在SQLAlchemy 1.2.6和Python3.6上进行了测试。

票数 27
EN

Stack Overflow用户

发布于 2017-11-27 23:33:03

现在有一个PyPI包可以做到这一点:SQLAlchemy Views

在it's PyPI页面上:

代码语言:javascript
运行
复制
>>> 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”,因此您可能希望使用SQLAlchemy query对象创建上面的definition

幸运的是,上面示例中的text()清楚地表明,CreateViewdefinition参数就是这样一个查询对象。所以像这样的东西应该是有效的:

代码语言:javascript
运行
复制
>>> 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)
...  )

下面是有趣的部分:

代码语言:javascript
运行
复制
>>> 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
票数 25
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9766940

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档