Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SQLAlchemy Create View in PostgresQL

I am trying create a view with SQLAlchemy with Postgresql as the underlying DB. The separate select query to create the view works well and returns results but when I use it in the create view, I get the error sqlalchemy.exc.NoSuchTableError: popular which means the view is not being selected. I am getting the error when I try to select from the view. Creating the view does not throw any error but it does not create the view. Here is my code:

from sqlalchemy import *
import sqlalchemy as db
from sqlalchemy import func
from sqlalchemy import desc
from sqlalchemy import Table
from sqlalchemy.ext.compiler import compiles
from sqlalchemy.sql.expression import Executable, ClauseElement

try:
    engine = db.create_engine('postgresql://user:pass@localhost:5432/db_name')
    connection = engine.connect()

except:
    print('Error establishing DB connection')

# Import metadata
metadata = db.MetaData()

# Import articles, authors and log tables
art = db.Table('articles', metadata, autoload=True, autoload_with=engine)
aut = db.Table('authors', metadata, autoload=True, autoload_with=engine)
log = db.Table('log', metadata, autoload=True, autoload_with=engine)


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)
         )



# Method to create view with top three articles
def view_top_three():
    top_three_view = CreateView('popular', db.select([art.columns.title, func.count(log.columns.path)]) \
        .where(func.concat('/article/', art.columns.slug) == log.columns.path) \
        .where(log.columns.path != "/") \
        .group_by(log.columns.path, art.columns.title) \
        .order_by(desc(func.count(log.columns.path))) \
        .limit(3))

    engine.execute(top_three_view)
    v = Table('popular', metadata, autoload=True, autoload_with=engine)
    for r in engine.execute(v.select()):
        print(r)


# Call the method which creates view and selects from view
view_top_three()

Any help will be appreciated.

like image 412
Benjamin Mwendwa Munyoki Avatar asked Jun 12 '26 12:06

Benjamin Mwendwa Munyoki


1 Answers

Since your CreateView inherits from Executable, and ClauseElement, it is not considered a data changing operation. In other words

engine.execute(top_three_view)

executes the CREATE VIEW statement and then implicitly rollbacks, when the connection is returned to the pool.

Instead it should be a subclass of DDLElement, as shown in the usage recipes wiki. Simply changing the baseclass will allow SQLAlchemy autocommit to work properly.

like image 147
Ilja Everilä Avatar answered Jun 15 '26 01:06

Ilja Everilä