Aktualizacja: Zobacz także przepis na użycie SQLAlchemy tutaj
Tworzenie niezmaterializowanego widoku (tylko do odczytu) nie jest obsługiwane po wyjęciu z pudełka, o ile wiem. Ale dodanie tej funkcjonalności w SQLAlchemy 0.7 jest proste (podobnie jak w przykładzie, który tu podałem ). Musisz tylko napisać rozszerzenie kompilatora CreateView
. Dzięki temu rozszerzeniu możesz następnie pisać (zakładając, że t
jest to obiekt tabeli z kolumną 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
Oto działający przykład:
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)
)
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))
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
Jeśli chcesz, możesz też specjalizować się w dialekcie, np
@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)
)
v = Table('viewname', metadata, autoload=True) class ViewName(object): def __init__(self, name): self.name = name mapper(ViewName, v)
powyżej jest możliwe? Ponieważ użyję View z sesją.orm.mapper(ViewName, v, primary_key=pk, properties=prop)
gdziepk
iprop
są odpowiednio klucz podstawowy (lub klucze) i właściwości. Zobacz docs.sqlalchemy.org/en/latest/orm/… .v = Table('viewname', metadata, Column('my_id_column', Integer, primary_key=True), autoload=True)
Odpowiedź Stephana jest dobra i obejmuje większość baz, ale niezadowolony był brak integracji z resztą SQLAlchemy (ORM, automatyczne usuwanie itp.). Po godzinach eksperymentowania i zbierania wiedzy ze wszystkich zakątków internetu wymyśliłem:
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'
Zwróć uwagę, że korzystam z
sqlalchemy_views
pakietu, aby uprościć sprawę.Definiowanie widoku (np. Globalnie jak modele Table):
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]
Mapowanie widoków (włącz funkcjonalność ORM):
Zrób to podczas ładowania aplikacji, przed zapytaniami i po skonfigurowaniu bazy danych.
for view in views: if not hasattr(view, '_sa_class_manager'): orm.mapper(view, view.__view__)
Tworzenie widoków:
Zrób podczas inicjalizacji bazy danych, np. Po wywołaniu create_all ().
from sqlalchemy import orm for view in views: db.engine.execute(CreateView(view))
Jak zapytać o widok:
results = db.session.query(SomeModel, SampleView).join( SampleView, SomeModel.id == SampleView.some_model_id ).all()
To zwróci dokładnie to, czego oczekujesz (listę obiektów, z których każdy ma obiekt SomeModel i obiekt SampleView).
Upuszczanie widoku:
SampleView.__view__.drop(db.engine)
Zostanie również automatycznie usunięty podczas wywołania drop_all ().
To oczywiście bardzo hakerskie rozwiązanie, ale moim zdaniem jest to obecnie najlepsze i najczystsze rozwiązanie. Testowałem to przez ostatnie kilka dni i nie miałem żadnych problemów. Nie jestem pewien, jak dodać relacje (napotkałem tam problemy), ale nie jest to konieczne, jak pokazano powyżej w zapytaniu.
Jeśli ktoś ma jakieś uwagi, znajdzie jakieś nieoczekiwane problemy lub zna lepszy sposób na zrobienie czegoś, zostaw komentarz lub daj mi znać.
Zostało to przetestowane w SQLAlchemy 1.2.6 i Python 3.6.
źródło
super(CreateView, self).__init__
i posiadająceclass SampleView(object)
Base = declarative_base(metadata=db.MetaData()) class ViewSample(Base): __tablename__ = 'view_sample'
nadal uwzględniłem__definition__
właściwość i wywołałem CreateView, aby utworzyć ją zgodnie z sugestią w oryginalnym poście. Na koniec musiałem zmodyfikować metodę dekorowanego upuszczania:if element.element.name.startswith('view_'): return compiler.visit_drop_view(element)
ponieważ nie mogłem znaleźć sposobu na dodanie właściwości do tabeli osadzonej.Obecnie jest do tego pakiet PyPI: SQLAlchemy Views .
Z jego strony 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
Jednak poprosiłeś o zapytanie „czystego SQL” , więc prawdopodobnie chcesz, aby
definition
powyższe zostało utworzone za pomocą obiektu zapytania SQLAlchemy.Na szczęście
text()
w powyższym przykładzie widać, żedefinition
parametr toCreateView
jest takim obiektem zapytania. Więc coś takiego powinno działać:>>> 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) ... )
Oto interesujący fragment:
>>> 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
źródło
SQLAlchemy-utils właśnie dodało tę funkcjonalność w wersji 0.33.6 (dostępna w pypi). Ma poglądy, zmaterializowane widoki i integruje się z ORM. Nie jest to jeszcze udokumentowane, ale z powodzeniem używam widoków + ORM.
Możesz użyć ich testu jako przykładu zarówno dla zwykłych, jak i zmaterializowanych widoków przy użyciu ORM.
Aby utworzyć widok, po zainstalowaniu pakietu użyj następującego kodu z powyższego testu jako podstawy widoku:
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 )
Gdzie
Base
jestdeclarative_base
,sa
jestSQLAlchemy
pakietem icreate_view
jest funkcją zsqlalchemy_utils.view
.źródło
Nie mogłem znaleźć krótkiej i przydatnej odpowiedzi.
Nie potrzebuję dodatkowej funkcjonalności widoku (jeśli w ogóle), więc po prostu traktuję widok jako zwykłą tabelę, tak jak inne definicje tabel.
Więc w zasadzie mam
a.py
gdzie definiuje wszystkie tabele i widoki, rzeczy związane z sql imain.py
gdzie importuję te klasya.py
i używam ich.Oto, co dodam
a.py
i działa:class A_View_From_Your_DataBase(Base): __tablename__ = 'View_Name' keyword = Column(String(100), nullable=False, primary_key=True)
Warto zauważyć, że musisz dodać
primary_key
właściwość, mimo że w widoku nie ma klucza podstawowego.źródło
Widok SQL bez czystego SQL? Możesz utworzyć klasę lub funkcję, aby zaimplementować zdefiniowany widok.
function get_view(con): return Table.query.filter(Table.name==con.name).first()
źródło