SQLAlchemy के साथ SQL व्यू कैसे बनाएं?


83

SQLAlchemy के साथ SQL दृश्य को परिभाषित करने के लिए "पायथोनिक" तरीका है (मेरा मतलब है, कोई "शुद्ध SQL" क्वेरी)?

जवाबों:


69

अद्यतन: यहां SQLAlchemy उपयोग नुस्खा भी देखें

जहाँ तक मुझे पता है बॉक्स से बाहर (केवल पढ़ने के लिए गैर-भौतिकीकृत) दृश्य बनाना समर्थित नहीं है। लेकिन SQLAlchemy 0.7 में इस कार्यक्षमता जोड़ने सीधा है (उदाहरण के मैं दे दी है के लिए इसी तरह यहाँ )। आपको बस एक कंपाइलर एक्सटेंशन लिखना है 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)
         )

क्या मैं orm.mapper के साथ मैप v का उपयोग कर सकता हूं? v = Table('viewname', metadata, autoload=True) class ViewName(object): def __init__(self, name): self.name = name mapper(ViewName, v) ऊपर की तरह संभव है? क्योंकि मैं सत्र के साथ View का उपयोग करूंगा।
सैयद हबीब एम

1
@ सैयद हबीब: हाँ, यह संभव है। आपको प्राथमिक कुंजी को मैन्युअल रूप से सेट करना होगा, हालांकि, कुछ ऐसे orm.mapper(ViewName, v, primary_key=pk, properties=prop)जहां pkऔर जहां propआपकी प्राथमिक कुंजी (या चाबियाँ) और गुण क्रमशः हैं। Docs.sqlalchemy.org/en/latest/orm/… देखें ।
चरणन

2
@SyedHabibM: तुम क्या कर सकते स्टीफ़न एक स्तंभ विनिर्देश अधिभावी द्वारा यह भी उल्लेख किया है जब आप autoloaded तालिकाओं का उपयोग और एक पी निर्दिष्ट करें:v = Table('viewname', metadata, Column('my_id_column', Integer, primary_key=True), autoload=True)
वैन

@SyedHabibMI ने आपके संबंधित प्रश्न का जवाब दिया stackoverflow.com/q/20518521/92092 अब एक कार्यकारी उदाहरण के साथ। मैं वहाँ भी वैन की टिप्पणी जोड़ूंगा।
स्टेपहान

27

स्टेफ़न का उत्तर एक अच्छा है और अधिकांश आधारों को शामिल करता है, लेकिन जो मुझे असंतुष्ट छोड़ गया था, वह 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 कार्यक्षमता सक्षम करें):

अपने एप्लिकेशन को लोड करते समय, किसी भी प्रश्न से पहले और डीबी सेट करने के बाद करें।

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.__view__.drop(db.engine)

यह भी एक drop_all () कॉल के दौरान स्वचालित रूप से हटा दिया जाएगा।

यह स्पष्ट रूप से एक बहुत ही आसान उपाय है, लेकिन मेरी नजर में यह सबसे अच्छा है और इस समय वहां से सबसे साफ है। मैंने पिछले कुछ दिनों में इसका परीक्षण किया है और कोई समस्या नहीं हुई है। मुझे यकीन नहीं है कि रिश्तों में कैसे जोड़ना है (वहाँ समस्याओं में भाग गया) लेकिन यह वास्तव में आवश्यक नहीं है, जैसा कि क्वेरी में ऊपर दिखाया गया है।

यदि किसी के पास कोई इनपुट है, तो कोई भी अप्रत्याशित समस्या पाता है, या चीजों को करने का एक बेहतर तरीका जानता है, कृपया एक टिप्पणी छोड़ दें या मुझे बताएं।

यह SQLAlchemy 1.2.6 और पायथन 3.6 पर परीक्षण किया गया था।


क्रेजी टाइमिंग, बस खुद के साथ यह व्यवहार कर रहा था। Py 2.7 और SQLa 1.1.2 के लिए (मत पूछो ...), केवल आवश्यक परिवर्तन थे super(CreateView, self).__init__और होनेclass SampleView(object)
स्टीवन डिकिंसन

1
@Steven डिकिंसन यूप सही के बारे में लगता है! हाँ, मुझे लगा कि यह वास्तव में एक सामान्य कार्य है, यही वजह है कि मुझे आश्चर्य हुआ कि इस पर प्रलेखन इतना खराब / पुराना / उथला था। लेकिन हे, एक समय में एक कदम मुझे लगता है।
fgblomqvist

2
यह घोषित रूप से करने की चाह रखने वालों के लिए, मैंने अपने विचारों को एक अलग मेटाडेटा उदाहरण के साथ अपनी तालिकाओं से अलग फ़ाइल में परिभाषित किया: Base = declarative_base(metadata=db.MetaData()) class ViewSample(Base): __tablename__ = 'view_sample' मैंने अभी भी __definition__संपत्ति को शामिल किया और मूल पोस्ट में सुझाए अनुसार इसे बनाने के लिए CreateView को बुलाया। अंत में, मुझे ड्रॉप सजाए गए तरीके को संशोधित करना पड़ा: if element.element.name.startswith('view_'): return compiler.visit_drop_view(element) क्योंकि मुझे संपत्ति को एम्बेडेड तालिका में जोड़ने का कोई तरीका नहीं मिला।
केसी

23

इन दिनों उस के लिए एक 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

हालाँकि, आपने कोई "शुद्ध SQL" क्वेरी नहीं मांगी है , इसलिए आप संभवतः definitionSQLAlchemy क्वेरी ऑब्जेक्ट के साथ बनाया जाना चाहते हैं ।

सौभाग्य से, 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

17

SQLAlchemy-utils ने केवल 0.33.6 में यह कार्यक्षमता जोड़ी (pypi में उपलब्ध)। इसमें दृश्य, भौतिक विचार हैं, और यह 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


क्या आपको एलेम्बिक के साथ मिलकर इसका उपयोग करने का एक तरीका मिल गया है?
जॉर्ज लेइताओ

1

मुझे एक छोटा और आसान जवाब नहीं मिला।

मुझे दृश्य की अतिरिक्त कार्यक्षमता की आवश्यकता नहीं है (यदि कोई है), तो मैं बस एक दृश्य को अन्य तालिका परिभाषाओं के रूप में एक साधारण तालिका के रूप में मानता हूं।

इसलिए मूल रूप से मेरे पास a.pyजहां सभी तालिकाओं और विचारों को परिभाषित करता है, एसक्यूएल से संबंधित सामान, और main.pyजहां मैं उन कक्षाओं को आयात करता हूं a.pyऔर उनका उपयोग करता हूं।

यहाँ मैं क्या जोड़ रहा हूँ a.pyऔर काम करता है:

class A_View_From_Your_DataBase(Base):
    __tablename__ = 'View_Name'
    keyword = Column(String(100), nullable=False, primary_key=True)

विशेष रूप से, आपको primary_keyसंपत्ति जोड़ने की आवश्यकता है, भले ही दृश्य में कोई प्राथमिक कुंजी न हो।


-9

शुद्ध SQL के बिना SQL देखें? आप एक परिभाषित दृश्य को लागू करने के लिए एक वर्ग या फ़ंक्शन बना सकते हैं।

function get_view(con):
  return Table.query.filter(Table.name==con.name).first()

2
क्षमा करें, लेकिन ऐसा नहीं है जो मैंने पूछा। मेरी अंग्रेजी सही नहीं है, अगर आप गलत समझे तो मुझे क्षमा करें :)
Thibaut D.
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.