SQLAlchemy ile bir SQL görünümünü tanımlamanın bir "Pythonic" yolu var mı (yani "saf SQL" sorgusu yok)?
SQLAlchemy ile bir SQL görünümünü tanımlamanın bir "Pythonic" yolu var mı (yani "saf SQL" sorgusu yok)?
Yanıtlar:
Güncelleme: Ayrıca burada SQLAlchemy kullanım tarifine bakın
Bildiğim kadarıyla bir (salt okunur materyalleştirilmemiş) görünüm oluşturmak, kutunun dışında desteklenmiyor. Ancak bu işlevselliği SQLAlchemy 0.7'de eklemek basittir ( burada verdiğim örneğe benzer ). Bir derleyici uzantısı yazmanız yeterlidir CreateView
. Bu uzantı ile daha sonra yazabilirsiniz (bunun t
bir sütun içeren bir tablo nesnesi olduğunu varsayarak 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
İşte çalışan bir örnek:
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
İsterseniz bir lehçe için de uzmanlaşabilirsiniz, örneğin
@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(ViewName, v, primary_key=pk, properties=prop)
nerede pk
ve prop
hangisidir? Docs.sqlalchemy.org/en/latest/orm/… bakın .
v = Table('viewname', metadata, Column('my_id_column', Integer, primary_key=True), autoload=True)
stephan'ın cevabı iyi bir cevap ve çoğu temeli kapsıyor, ancak beni tatmin etmeyen şey, SQLAlchemy'nin geri kalanıyla (ORM, otomatik bırakma vb.) entegrasyon eksikliğiydi. İnternetin her köşesinden gelen bilgileri saatlerce denedikten ve bir araya getirdikten sonra aşağıdakileri buldum:
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
Paketi sadece işleri basitleştirmek için kullandığımı unutmayın .
Bir görünüm tanımlama (örneğin, genel olarak Masa modelleriniz gibi):
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]
Görünümlerin eşlenmesi (ORM işlevselliğini etkinleştirin):
Uygulamanızı yüklerken, herhangi bir sorudan önce ve DB'yi kurduktan sonra yapın.
for view in views:
if not hasattr(view, '_sa_class_manager'):
orm.mapper(view, view.__view__)
Görünümlerin oluşturulması:
Veritabanını başlatırken yapın, örneğin bir create_all () çağrısından sonra.
from sqlalchemy import orm
for view in views:
db.engine.execute(CreateView(view))
Bir görünüm nasıl sorgulanır:
results = db.session.query(SomeModel, SampleView).join(
SampleView,
SomeModel.id == SampleView.some_model_id
).all()
Bu, tam olarak beklediğiniz şeyi döndürür (her biri bir SomeModel nesnesi ve bir SampleView nesnesi olan nesnelerin bir listesi).
Bir görünümü düşürmek:
SampleView.__view__.drop(db.engine)
Drop_all () çağrısı sırasında da otomatik olarak düşecektir.
Bu kesinlikle çok karmaşık bir çözüm ama benim gözümde şu anda piyasadaki en iyisi ve en temiz olanı. Geçtiğimiz birkaç gün içinde test ettim ve herhangi bir sorun yaşamadım. İlişkileri nasıl ekleyeceğimi bilmiyorum (orada sorunlarla karşılaştım), ancak yukarıda sorguda gösterildiği gibi gerçekten gerekli değil.
Herhangi birinin herhangi bir girdisi varsa, beklenmedik sorunlar bulursa veya işleri yapmanın daha iyi bir yolunu bilen varsa, lütfen bir yorum bırakın veya bana bildirin.
Bu, SQLAlchemy 1.2.6 ve Python 3.6 üzerinde test edildi.
super(CreateView, self).__init__
ve sahip olmakclass SampleView(object)
Base = declarative_base(metadata=db.MetaData()) class ViewSample(Base): __tablename__ = 'view_sample'
yine de __definition__
özelliği ekledim ve orijinal gönderide önerildiği gibi oluşturmak için CreateView'ı çağırdım. Son olarak, damla dekore edilmiş yöntemi değiştirmek zorunda kaldım: if element.element.name.startswith('view_'): return compiler.visit_drop_view(element)
çünkü özelliği gömülü tabloya eklemenin bir yolunu bulamadım.
Bugünlerde bunun için bir PyPI paketi var: SQLAlchemy Views .
PyPI Sayfasından:
>>> 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
Ancak, "saf SQL" sorgusu istemediniz, bu nedenle muhtemelen definition
yukarıdakinin SQLAlchemy sorgu nesnesiyle oluşturulmasını istiyorsunuz .
Neyse ki, text()
yukarıdaki örnekte, to definition
parametresinin CreateView
böyle bir sorgu nesnesi olduğunu açıkça ortaya koymaktadır . Yani bunun gibi bir şey çalışmalı:
>>> 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)
... )
İşte ilginç olan kısım:
>>> 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 bu işlevselliği 0.33.6'da ekledi (pypi'de mevcuttur). Görüşlere, somutlaşmış görüşlere sahiptir ve ORM ile bütünleşir. Henüz belgelenmedi, ancak görünümleri + ORM'yi başarıyla kullanıyorum.
Sen edebilirsiniz örnek olarak onların testini kullanmak hem normal ve ORM kullanarak görüşlerin kullanımı.
Bir görünüm oluşturmak için, paketi yükledikten sonra, görünümünüz için temel olarak yukarıdaki testte bulunan aşağıdaki kodu kullanın:
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
)
Nerede Base
olduğunu declarative_base
, sa
bir SQLAlchemy
paket ve create_view
bir fonksiyondur sqlalchemy_utils.view
.
Kısa ve kullanışlı bir cevap bulamadım.
Ekstra Görünüm işlevselliğine ihtiyacım yok (eğer varsa), bu nedenle bir görünümü sıradan bir tablo olarak diğer tablo tanımları gibi ele alıyorum.
Yani temelde a.py
tüm tabloları ve görünümleri, sql ile ilgili şeyleri tanımladığım ve main.py
bu sınıfı nereden aktardığım a.py
ve kullandığım yer var.
İşte eklediğim a.py
ve işe yarayan şey:
class A_View_From_Your_DataBase(Base):
__tablename__ = 'View_Name'
keyword = Column(String(100), nullable=False, primary_key=True)
Özellikle, primary_key
görünümde birincil anahtar olmasa bile özelliği eklemeniz gerekir .
Saf SQL olmadan SQL Görünümü? Tanımlı bir görünümü uygulamak için bir sınıf veya işlev oluşturabilirsiniz.
function get_view(con):
return Table.query.filter(Table.name==con.name).first()
v = Table('viewname', metadata, autoload=True) class ViewName(object): def __init__(self, name): self.name = name mapper(ViewName, v)
Yukarıdaki gibi mümkün mü? Çünkü View'u oturum ile kullanacağım.