On 7/8/15 9:25 PM, Vincent Catalano wrote:
I've been trying to get SQLAlchemy to work with Postgres composite
types for the last couple days with no luck. I've implemented the
PGCompositeType that Michael created in another post
(https://groups.google.com/forum/#!searchin/sqlalchemy/postgres$20composite/sqlalchemy/f9BPVHfdvbg/HqEbDUO9UOMJ),
however, I cannot seem to get this to work. I'm also a bit unclear on
how to actually query based on the composite type (I'm using
declarative table definitions).
Well that email only went as far as writing SQL expressions.
Here is more detail added which starts to be able to query these types.
We must use psycopg2s facilities for doing so, described at
http://initd.org/psycopg/docs/extras.html#composite-types-casting.
This is only a rudimentary example and can use a lot of improvement.
For joining, you can use it in join of the form query.join(YourClass,
YourClass.composite_attr.attrs.whatever == OtherClass.whatever).
from sqlalchemy.ext.compiler import compiles
from sqlalchemy.types import UserDefinedType, to_instance
from sqlalchemy.sql.expression import ColumnElement, Tuple, bindparam
class PGCompositeElement(ColumnElement):
def __init__(self, base, attrname, type_):
self.base = base
self.attrname = attrname
self.type = to_instance(type_)
@compiles(PGCompositeElement)
def _compile_pgelem(element, compiler, **kw):
return "%s.%s" % (
compiler.process(element.base, **kw),
element.attrname
)
class ROW(Tuple):
pass
@compiles(ROW)
def _compile_row(element, compiler, **kw):
return "ROW(%s)" % (compiler.process(super(ROW, element), **kw))
class _Namespace(object):
def __init__(self, comparator):
self.comparator = comparator
def __getattr__(self, key):
try:
type_ = self.comparator.type.typemap[key]
except KeyError:
raise KeyError(
"Type '%s' doesn't have an attribute: '%s'" %
(self.comparator.type, key))
return PGCompositeElement(
self.comparator.expr,
key,
type_)
class PGCompositeType(UserDefinedType):
def __init__(self, name, typemap):
self.name = name
self.attrnames = [elem[0] for elem in typemap]
self.typemap = dict(typemap)
class comparator_factory(UserDefinedType.Comparator):
@property
def attrs(self):
return _Namespace(self)
def get_col_spec(self):
return self.name
if __name__ == '__main__':
from sqlalchemy import String, Integer, create_engine, Column
from sqlalchemy.orm import Session
from sqlalchemy import event
mytype = PGCompositeType(
"mytype", [("attr1", String(50)), ("attr2", Integer)])
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class SomeClass(Base):
__tablename__ = 'sometable'
id = Column(Integer, primary_key=True)
composite_data = Column(mytype)
e = create_engine("postgresql://scott:tiger@localhost/test", echo=True)
conn = e.connect()
t = conn.begin()
conn.execute("create type mytype as (attr1 varchar(50), attr2
integer);")
import psycopg2
@event.listens_for(conn, 'before_cursor_execute')
def before_cursor_execute(
conn, cursor, statement, parameters, context, executemany):
psycopg2.extras.register_composite('mytype', cursor)
Base.metadata.create_all(conn)
s = Session(conn)
obj = SomeClass(composite_data=ROW("foo", 50))
s.add(obj)
s.commit()
o1 = s.query(SomeClass).first()
assert o1.composite_data == ('foo', 50)
Here is what I have so far - the column definition is taken from
Michael Bayer's post:
from sqlalchemy.ext.compiler import compiles
from sqlalchemy.types import UserDefinedType, to_instance
from sqlalchemy.sql.expression import ColumnElement
class PGCompositeElement(ColumnElement):
def __init__(self, base, field, type_):
self.name = field
super(PGCompositeElement, self).__init__(base)
self.type = to_instance(type_)
@compiles(PGCompositeElement)
def _compile_pgelem(expr, compiler, **kw):
return '(%s).%s' % (compiler.process(expr.clauses, **kw), expr.name)
class _Namespace(object):
def __init__(self, comparator):
self.comparator = comparator
def __getattr__(self, key):
try:
type_ = self.comparator.type.typemap[key]
except KeyError:
raise KeyError(
"Type '%s' doesn't have an attribute: '%s'" %
(self.comparator.type, key))
return PGCompositeElement(self.comparator.expr, key, type_)
class PGCompositeType(UserDefinedType):
def __init__(self, name, typemap):
self.name = name
self.typemap = typemap
class comparator_factory(UserDefinedType.Comparator):
@property
def attrs(self):
return _Namespace(self)
def get_col_spec(self):
return self.name
# My column and tables definitions:
TopoGeom = PGCompositeType('topo_geom', {'topology_id': Integer,
'layer_id': Integer, 'id': Integer, 'type': Integer})
class Trail(Base):
__tablename__ = 'trail'
id = Column(Integer, primary_key=True)
topo_geom = Column(TopoGeom)
When I query using the following, I get an attribute error since
/topo_geom/ is a string.
result = self.session.query(Trail).first()
print result.topo_geom.attrs.topology_id
How do I actually get the column attributes from a result (for
example, /topology_id/)? Additionally, is it possible to query and
join other tables based on the composite fields?
--
You received this message because you are subscribed to the Google
Groups "sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send
an email to [email protected]
<mailto:[email protected]>.
To post to this group, send email to [email protected]
<mailto:[email protected]>.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.