-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
PGValues
mike bayer edited this page Oct 17, 2022
·
7 revisions
Note - SQLAlchemy includes the SQL "VALUES" construct built in as of the 1.4 series:
A modern 1.4/2.0 compatible version of the original example is as follows:
from sqlalchemy import Column
from sqlalchemy import create_engine
from sqlalchemy import Integer
from sqlalchemy import MetaData
from sqlalchemy import String
from sqlalchemy import values
from sqlalchemy.orm import declarative_base
from sqlalchemy.orm import Session
from sqlalchemy.sql import column
Base = declarative_base()
m1 = MetaData()
class T(Base):
__tablename__ = "mytable"
mykey = Column(Integer, primary_key=True)
mytext = Column(String(50))
myint = Column(Integer)
t2 = values(
column("mykey", Integer),
column("mytext", String),
column("myint", Integer),
name="myvalues",
).data(
[
(1, "textA", 99),
(2, "textB", 88),
]
)
e = create_engine("postgresql://scott:tiger@localhost/test", echo=True)
m1.create_all(e)
sess = Session(e)
sess.query(T).filter(T.mykey == t2.c.mykey).update(
{"mytext": t2.c.mytext, "myint": t2.c.myint},
synchronize_session=False
)
This recipe produces a Postgresql VALUES
construct.
from sqlalchemy.ext.compiler import compiles
from sqlalchemy.sql.expression import FromClause
class values(FromClause):
named_with_column = True
def __init__(self, columns, *args, **kw):
self._column_args = columns
self.list = args
self.alias_name = self.name = kw.pop("alias_name", None)
def _populate_column_collection(self):
for c in self._column_args:
c._make_proxy(self)
@property
def _from_objects(self):
return [self]
@compiles(values)
def compile_values(element, compiler, asfrom=False, **kw):
columns = element.columns
v = "VALUES %s" % ", ".join(
"(%s)"
% ", ".join(
compiler.render_literal_value(elem, column.type)
for elem, column in zip(tup, columns)
)
for tup in element.list
)
if asfrom:
if element.alias_name:
v = "(%s) AS %s (%s)" % (
v,
element.alias_name,
(", ".join(c.name for c in element.columns)),
)
else:
v = "(%s)" % v
return v
if __name__ == "__main__":
from sqlalchemy import (
MetaData,
create_engine,
String,
Integer,
Table,
Column,
)
from sqlalchemy.sql import column
from sqlalchemy.orm import Session, mapper
m1 = MetaData()
class T(object):
pass
t1 = Table(
"mytable",
m1,
Column("mykey", Integer, primary_key=True),
Column("mytext", String),
Column("myint", Integer),
)
mapper(T, t1)
t2 = values(
[
column("mykey", Integer),
column("mytext", String),
column("myint", Integer),
],
(1, "textA", 99),
(2, "textB", 88),
alias_name="myvalues",
)
e = create_engine("postgresql://scott:tiger@localhost/test", echo=True)
m1.create_all(e)
sess = Session(e)
sess.query(T).filter(T.mykey == t2.c.mykey).update(
dict(mytext=t2.c.mytext, myint=t2.c.myint)
)
The query at the end renders:
UPDATE mytable SET mytext=myvalues.mytext, myint=myvalues.myint
FROM (VALUES (1, 'textA', 99), (2, 'textB', 88)) AS myvalues (mykey, mytext, myint)
WHERE mytable.mykey = myvalues.mykey