Có cách nào "Pythonic" (ý tôi là không có truy vấn "thuần SQL") để xác định chế độ xem SQL với SQLAlchemy?
Có cách nào "Pythonic" (ý tôi là không có truy vấn "thuần SQL") để xác định chế độ xem SQL với SQLAlchemy?
Câu trả lời:
Cập nhật: Xem thêm công thức sử dụng SQLAlchemy tại đây
Theo tôi biết, việc tạo chế độ xem (chỉ đọc, không được hiện thực hóa) không được hỗ trợ. Nhưng việc thêm chức năng này trong SQLAlchemy 0.7 rất đơn giản (tương tự như ví dụ tôi đã đưa ra ở đây ). Bạn chỉ cần viết một phần mở rộng trình biên dịch CreateView
. Với phần mở rộng này, sau đó bạn có thể viết (giả sử đó t
là một đối tượng bảng có một cộ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
Đây là một ví dụ hoạt động:
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
Nếu bạn muốn, bạn cũng có thể chuyên về một phương ngữ, ví dụ:
@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)
vị trí pk
và prop
khóa chính (hoặc các khóa) và thuộc tính của bạn tương ứng. Xem docs.sqlalchemy.org/en/latest/orm/… .
v = Table('viewname', metadata, Column('my_id_column', Integer, primary_key=True), autoload=True)
Câu trả lời của stephan là một câu trả lời hay và bao gồm hầu hết các cơ sở, nhưng điều khiến tôi không hài lòng là thiếu tích hợp với phần còn lại của SQLAlchemy (ORM, tự động thả, v.v.). Sau nhiều giờ thử nghiệm và tổng hợp kiến thức từ tất cả các góc của internet, tôi đã nghĩ ra những điều sau:
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'
Lưu ý rằng tôi đang sử dụng sqlalchemy_views
gói, chỉ để đơn giản hóa mọi thứ.
Xác định chế độ xem (ví dụ toàn cầu như các mô hình Bảng của bạn):
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]
Ánh xạ các chế độ xem (bật chức năng ORM):
Thực hiện khi tải ứng dụng của bạn, trước bất kỳ truy vấn nào và sau khi thiết lập DB.
for view in views:
if not hasattr(view, '_sa_class_manager'):
orm.mapper(view, view.__view__)
Tạo các khung nhìn:
Thực hiện khi khởi tạo cơ sở dữ liệu, ví dụ sau khi gọi lệnh create_all ().
from sqlalchemy import orm
for view in views:
db.engine.execute(CreateView(view))
Cách truy vấn một dạng xem:
results = db.session.query(SomeModel, SampleView).join(
SampleView,
SomeModel.id == SampleView.some_model_id
).all()
Điều này sẽ trả về chính xác những gì bạn mong đợi (danh sách các đối tượng mà mỗi đối tượng có một đối tượng SomeModel và một đối tượng SampleView).
Bỏ chế độ xem:
SampleView.__view__.drop(db.engine)
Nó cũng sẽ tự động bị loại bỏ trong cuộc gọi drop_all ().
Đây rõ ràng là một giải pháp rất khó hiểu nhưng trong mắt tôi, nó là giải pháp tốt nhất và sạch nhất hiện tại. Tôi đã thử nghiệm nó vài ngày qua và không có bất kỳ vấn đề nào. Tôi không chắc làm thế nào để thêm vào các mối quan hệ (đã gặp sự cố ở đó) nhưng nó không thực sự cần thiết, như đã trình bày ở trên trong truy vấn.
Nếu bất kỳ ai có bất kỳ ý kiến đóng góp nào, phát hiện bất kỳ vấn đề bất ngờ nào hoặc biết cách tốt hơn để thực hiện công việc, vui lòng để lại bình luận hoặc cho tôi biết.
Điều này đã được thử nghiệm trên SQLAlchemy 1.2.6 và Python 3.6.
super(CreateView, self).__init__
và cóclass SampleView(object)
Base = declarative_base(metadata=db.MetaData()) class ViewSample(Base): __tablename__ = 'view_sample'
Tôi vẫn bao gồm thuộc __definition__
tính và gọi CreateView để tạo nó như được đề xuất trong bài đăng ban đầu. Cuối cùng, tôi đã phải sửa đổi phương pháp thả trang trí: if element.element.name.startswith('view_'): return compiler.visit_drop_view(element)
bởi vì tôi không thể tìm cách thêm thuộc tính vào bảng nhúng.
Ngày nay, có một gói PyPI cho điều đó: Chế độ xem SQLAlchemy .
Từ Trang PyPI của nó:
>>> 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
Tuy nhiên, bạn đã yêu cầu không có truy vấn "thuần SQL" , vì vậy có thể bạn muốndefinition
ở trên được tạo bằng đối tượng truy vấn SQLAlchemy.
May mắn thay, text()
trong ví dụ trên cho thấy rõ rằng definition
tham số tới CreateView
là một đối tượng truy vấn. Vì vậy, một cái gì đó như thế này sẽ hoạt động:
>>> 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)
... )
Đây là một chút thú vị:
>>> 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 vừa thêm chức năng này trong 0.33.6 (có sẵn trong pypi). Nó có các khung nhìn, các khung nhìn cụ thể hóa và nó tích hợp với ORM. Nó chưa được lập thành tài liệu, nhưng tôi đang sử dụng thành công lượt xem + ORM.
Bạn có thể sử dụng thử nghiệm của họ làm ví dụ cho cả chế độ xem thông thường và chế độ xem cụ thể hóa bằng ORM.
Để tạo chế độ xem, sau khi bạn cài đặt gói, hãy sử dụng mã sau từ thử nghiệm ở trên làm cơ sở cho chế độ xem của bạ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
)
Trong trường hợp Base
là declarative_base
, sa
là SQLAlchemy
gói, và create_view
là một hàm từ sqlalchemy_utils.view
.
Tôi không thể tìm thấy câu trả lời ngắn gọn và tiện dụng.
Tôi không cần thêm chức năng của Chế độ xem (nếu có), vì vậy tôi chỉ đơn giản coi chế độ xem như một bảng bình thường như các định nghĩa bảng khác.
Vì vậy, về cơ bản tôi có a.py
nơi xác định tất cả các bảng và chế độ xem, những thứ liên quan đến sql và main.py
nơi tôi nhập các lớp đó từa.py
và sử dụng chúng.
Đây là những gì tôi thêm vào a.py
và hoạt động:
class A_View_From_Your_DataBase(Base):
__tablename__ = 'View_Name'
keyword = Column(String(100), nullable=False, primary_key=True)
Đáng chú ý, bạn cần thêm thuộc primary_key
tính mặc dù không có khóa chính trong chế độ xem.
SQL View không có SQL thuần túy? Bạn có thể tạo một lớp hoặc một hàm để triển khai một dạng xem đã xác định.
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)
trên là có thể? Bởi vì tôi sẽ sử dụng Chế độ xem với phiên.