Re: [sqlalchemy] from_statement and cte problem

2019-07-01 Thread Mike Bayer


On Mon, Jul 1, 2019, at 5:20 PM, sector119 wrote:
> For example I have a property 'path' with CTE like this and it wouldn't 
> return set of rows, but scalar value as array[] of rows
> would it be possible to load this property as subquery() ?

I don't know from a SQL perspective you'd likely have to use some PG array 
function that does that, I don't really use those functions because I don't 
really buy into PG's SQL extension culture. But anyway, if you can build a 
correlated scalar subquery that returns what you want, use column_property() 
and that will get it for you.

I can get the basic idea using func.array():

from sqlalchemy import Column
from sqlalchemy import create_engine
from sqlalchemy import ForeignKey
from sqlalchemy import func
from sqlalchemy import Integer
from sqlalchemy import select
from sqlalchemy import String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import column_property
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Session

Base = declarative_base()


class A(Base):
 __tablename__ = "a"

 id = Column(Integer, primary_key=True)
 data = Column(String)
 bs = relationship("B")


class B(Base):
 __tablename__ = "b"
 id = Column(Integer, primary_key=True)
 a_id = Column(ForeignKey("a.id"))
 data = Column(String)


A.b_data = column_property(
 func.array(select([B.data]).where(B.a_id == A.id).as_scalar())
)

e = create_engine("postgresql://scott:tiger@localhost/test", echo=True)
Base.metadata.drop_all(e)
Base.metadata.create_all(e)

s = Session(e)

s.add_all(
 [A(bs=[B(data="b1"), B(data="b2")]), A(bs=[B(data="b3"), B(data="b4")])]
)
s.commit()

for a1 in s.query(A):
 print(a1.b_data)





> 
> Of course I can include that CTE query in my query(Locality) using 
> subquery(), but it would be cool if I'll be able to "undefer" that property 
> somehow like relation )
> 
> @property
> def path(self):
>  session = object_session(self)
> 
>  def get_locality_path_q(locality_id):
>  parent_q = session.query(Locality).filter(Locality.id == 
> locality_id).cte(recursive=True)
> 
>  parents = aliased(parent_q)
> 
>  locality_alias = aliased(Locality)
> 
>  q = parent_q.union_all(
>  session.query(locality_alias).join(parents, locality_alias.id == 
> parents.c.parent_id)
>  )
> 
>  cte = aliased(Locality, q)
> 
>  return session.query(cte).order_by(cte.id)
> 
>  return get_locality_path_q(self.id)
> 
> вторник, 2 июля 2019 г., 0:03:20 UTC+3 пользователь Mike Bayer написал:
>> I don't know what you mean. is there SQL you know you want or is that the 
>> part you want to figure out?
>> 
>> 
>> On Mon, Jul 1, 2019, at 4:21 PM, sector119 wrote:
>>> Mike, is it possible to load "@property" as subquery? Maybe as 
>>> ARRAY[path_item, path_item, ...]
>>> 
>>> воскресенье, 30 июня 2019 г., 20:26:42 UTC+3 пользователь Mike Bayer 
>>> написал:
 
 
 On Sun, Jun 30, 2019, at 5:37 AM, sector119 wrote:
> Nice, thanks a lot, Mike, now it works as expected
 
 that's great. the docs are not good here, there's not enough discussion of 
 "aliased()" , from_statement() and what they do, also I'm changing some 
 details of how they do their work for 1.4 in any case so documentation 
 efforts will be needed.
 
 
 
> 
> @property
> *def *path(self):
> session = object_session(self)
> 
> *def *get_locality_path_q(locality_id):
> parent_q = session.query(Locality).filter(Locality.id == 
> locality_id).cte(recursive=*True*)
> 
> parents = aliased(parent_q)
> 
> locality_alias = aliased(Locality)
> 
> q = parent_q.union_all(
> session.query(locality_alias).join(parents, locality_alias.id 
> == parents.c.parent_id)
> )
> 
> cte = aliased(Locality, q)
> 
> *return *session.query(cte).order_by(cte.id)
> 
> *return *get_locality_path_q(self.id)
> 
> воскресенье, 30 июня 2019 г., 1:11:21 UTC+3 пользователь Mike Bayer 
> написал:
>> 
>> 
>> On Sat, Jun 29, 2019, at 11:24 AM, sector119 wrote:
>>> Hello,
>>> 
>>> I have Locality model with 'path' property to get path from 'root' of 
>>> tree to current item, everything works ok, but
>>> I can't get result as Locality instance list..
>>> When I use 
>>> *'*object_session(self).query(Locality).from_statement(q).order_by(Locality.id)'
>>>  I get 
>>> sqlalchemy.exc.ArgumentError: from_statement accepts text(), select(), 
>>> and union() objects only.
>>> 
>>> How can I adopt results to Locality model?
>>> 
>>> 
>>> *class *Locality(Base):
>>> __tablename__ = *'localities'
**
***__table_args__ = {*'schema'*: SYSTEM_SCHEMA}
>>> 
>>> id = Column(Integer, primary_key=*True*)
>>> parent_id = Column(Integer, ForeignKey(SYSTEM_SCHEMA + 
>>> *'.localities.id'*))
>>> name = Column(UnicodeText

Re: [sqlalchemy] from_statement and cte problem

2019-07-01 Thread sector119
For example I have a property 'path' with CTE like this and it wouldn't 
return set of rows, but scalar value as array[] of rows
would it be possible to load this property as subquery() ?

Of course I can include that CTE query in my query(Locality) using 
subquery(), but it would be cool if I'll be able to "undefer" that property 
somehow like relation )

@property
def path(self):
session = object_session(self)

def get_locality_path_q(locality_id):
parent_q = session.query(Locality).filter(Locality.id == 
locality_id).cte(recursive=True)

parents = aliased(parent_q)

locality_alias = aliased(Locality)

q = parent_q.union_all(
session.query(locality_alias).join(parents, locality_alias.id 
== parents.c.parent_id)
)

cte = aliased(Locality, q)

return session.query(cte).order_by(cte.id)

return get_locality_path_q(self.id)

вторник, 2 июля 2019 г., 0:03:20 UTC+3 пользователь Mike Bayer написал:
>
> I don't know what you mean.  is there SQL you know you want or is that the 
> part you want to figure out?
>
>
> On Mon, Jul 1, 2019, at 4:21 PM, sector119 wrote:
>
> Mike, is it possible to load "@property" as subquery? Maybe as 
> ARRAY[path_item, path_item, ...]
>
> воскресенье, 30 июня 2019 г., 20:26:42 UTC+3 пользователь Mike Bayer 
> написал:
>
>
>
> On Sun, Jun 30, 2019, at 5:37 AM, sector119 wrote:
>
> Nice, thanks a lot, Mike, now it works as expected
>
>
> that's great.  the docs are not good here, there's not enough discussion 
> of "aliased()" , from_statement() and what they do, also I'm changing some 
> details of how they do their work for 1.4 in any case so documentation 
> efforts will be needed.
>
>
>
>
> @property
> *def *path(self):
> session = object_session(self)
>
> *def *get_locality_path_q(locality_id):
> parent_q = session.query(Locality).filter(Locality.id == 
> locality_id).cte(recursive=*True*)
>
> parents = aliased(parent_q)
>
> locality_alias = aliased(Locality)
>
> q = parent_q.union_all(
> session.query(locality_alias).join(parents, locality_alias.id == 
> parents.c.parent_id)
> )
>
> cte = aliased(Locality, q)
>
> *return *session.query(cte).order_by(cte.id)
>
> *return *get_locality_path_q(self.id)
>
>
> воскресенье, 30 июня 2019 г., 1:11:21 UTC+3 пользователь Mike Bayer 
> написал:
>
>
>
> On Sat, Jun 29, 2019, at 11:24 AM, sector119 wrote:
>
> Hello,
>
>
> I have Locality model with 'path' property to get path from 'root' of tree to 
> current item, everything works ok, but
>
> I can't get result as Locality instance list..
>
> When I use 
> *'*object_session(self).query(Locality).from_statement(q).order_by(Locality.id)'
>  I get 
>
> sqlalchemy.exc.ArgumentError: from_statement accepts text(), select(), and 
> union() objects only.
>
>
> How can I adopt results to Locality model?
>
>
>
> *class *Locality(Base):
> __tablename__ = 
> *'localities'*
> __table_args__ = {*'schema'*: SYSTEM_SCHEMA}
>
> id = Column(Integer, primary_key=*True*)
> parent_id = Column(Integer, ForeignKey(SYSTEM_SCHEMA + *'.localities.id 
> '*))
> name = Column(UnicodeText, nullable=*False*)
> type = Column(Integer, nullable=*False*)
>
> @property
> *def *path(self):
> *def *get_locality_path_q(locality_id):
> top_q = select([
> Locality.id,
> Locality.parent_id,
> Locality.name,
> Locality.type,
> ]).\
> where(Locality.id == locality_id).\
> cte(recursive=*True*)
>
> parents = aliased(top_q)
>
> locality_alias = aliased(Locality)
>
> q = top_q.union_all(
> select([
> locality_alias.id,
> locality_alias.parent_id,
> locality_alias.name,
> locality_alias.type
> ]).select_from(join(locality_alias, parents, 
> locality_alias.id == parents.c.parent_id))
> )
>
> 
> *# return object_session(self).query(q).order_by(q.c.id )* 
>*return 
> *object_session(self).query(Locality).from_statement(q).order_by(Locality.id)
>
> *return *get_locality_path_q(self.id)
>
>
> above, "q" is a CTE, not a SELECT, meaning it's something you can select 
> FROM, like a table.  Call select() to SELECT from it:
>
> return (
> object_session(self)
> .query(Locality)
> .from_statement(q.select().order_by(q.c.id))
> )
>
>
> additionally, from_statement() does not allow further changes to the 
> statement and the ORDER BY must be in terms of thing you are selecting 
> from, in this case q.c.id
>
> your code would be easier if you made use of top_q as a FROM object rather 
> than a statement:
>
> lcte = aliased(Locali

Re: [sqlalchemy] from_statement and cte problem

2019-07-01 Thread Mike Bayer
I don't know what you mean. is there SQL you know you want or is that the part 
you want to figure out?


On Mon, Jul 1, 2019, at 4:21 PM, sector119 wrote:
> Mike, is it possible to load "@property" as subquery? Maybe as 
> ARRAY[path_item, path_item, ...]
> 
> воскресенье, 30 июня 2019 г., 20:26:42 UTC+3 пользователь Mike Bayer написал:
>> 
>> 
>> On Sun, Jun 30, 2019, at 5:37 AM, sector119 wrote:
>>> Nice, thanks a lot, Mike, now it works as expected
>> 
>> that's great. the docs are not good here, there's not enough discussion of 
>> "aliased()" , from_statement() and what they do, also I'm changing some 
>> details of how they do their work for 1.4 in any case so documentation 
>> efforts will be needed.
>> 
>> 
>> 
>>> 
>>> @property
>>> *def *path(self):
>>> session = object_session(self)
>>> 
>>> *def *get_locality_path_q(locality_id):
>>> parent_q = session.query(Locality).filter(Locality.id == 
>>> locality_id).cte(recursive=*True*)
>>> 
>>> parents = aliased(parent_q)
>>> 
>>> locality_alias = aliased(Locality)
>>> 
>>> q = parent_q.union_all(
>>> session.query(locality_alias).join(parents, locality_alias.id 
>>> == parents.c.parent_id)
>>> )
>>> 
>>> cte = aliased(Locality, q)
>>> 
>>> *return *session.query(cte).order_by(cte.id)
>>> 
>>> *return *get_locality_path_q(self.id)
>>> 
>>> воскресенье, 30 июня 2019 г., 1:11:21 UTC+3 пользователь Mike Bayer написал:
 
 
 On Sat, Jun 29, 2019, at 11:24 AM, sector119 wrote:
> Hello,
> 
> I have Locality model with 'path' property to get path from 'root' of 
> tree to current item, everything works ok, but
> I can't get result as Locality instance list..
> When I use 
> *'*object_session(self).query(Locality).from_statement(q).order_by(Locality.id)'
>  I get 
> sqlalchemy.exc.ArgumentError: from_statement accepts text(), select(), 
> and union() objects only.
> 
> How can I adopt results to Locality model?
> 
> 
> *class *Locality(Base):
> __tablename__ = *'localities'
**
***__table_args__ = {*'schema'*: SYSTEM_SCHEMA}
> 
> id = Column(Integer, primary_key=*True*)
> parent_id = Column(Integer, ForeignKey(SYSTEM_SCHEMA + 
> *'.localities.id'*))
> name = Column(UnicodeText, nullable=*False*)
> type = Column(Integer, nullable=*False*)
> 
> @property
> *def *path(self):
> *def *get_locality_path_q(locality_id):
> top_q = select([
> Locality.id,
> Locality.parent_id,
> Locality.name,
> Locality.type,
> ]).\
> where(Locality.id == locality_id).\
> cte(recursive=*True*)
> 
> parents = aliased(top_q)
> 
> locality_alias = aliased(Locality)
> 
> q = top_q.union_all(
> select([
> locality_alias.id,
> locality_alias.parent_id,
> locality_alias.name,
> locality_alias.type
> ]).select_from(join(locality_alias, parents, 
> locality_alias.id == parents.c.parent_id))
> )
> 
> *# return object_session(self).query(q).order_by(q.c.id)
****return 
*object_session(self).query(Locality).from_statement(q).order_by(Locality.id)
> 
> *return *get_locality_path_q(self.id)
 
 above, "q" is a CTE, not a SELECT, meaning it's something you can select 
 FROM, like a table. Call select() to SELECT from it:
 
  return (
  object_session(self)
  .query(Locality)
  .from_statement(q.select().order_by(q.c.id))
  )
 
 
 additionally, from_statement() does not allow further changes to the 
 statement and the ORDER BY must be in terms of thing you are selecting 
 from, in this case q.c.id
 
 your code would be easier if you made use of top_q as a FROM object rather 
 than a statement:
 
  lcte = aliased(Locality, q)
 
  return (
  object_session(self)
  .query(lcte)
  .order_by(lcte.id)
  )
 
 
 There's not too many doc examples of how aliased() works with FROM clauses 
 but the basic idea is at:
 
 https://docs.sqlalchemy.org/en/13/orm/tutorial.html#selecting-entities-from-subqueries
 
 https://docs.sqlalchemy.org/en/13/orm/query.html#sqlalchemy.orm.aliased
 
 A little more on aliased() with CTE is written more for query.cte() which 
 you could also be using here:
 
 https://docs.sqlalchemy.org/en/13/orm/query.html?highlight=cte#sqlalchemy.orm.query.Query.cte
 
 
 
 
> 
> 

> --
> SQLAlchemy - 
> The Python SQL Toolkit and Object Relational Mapper
> 
> http://

Re: [sqlalchemy] from_statement and cte problem

2019-07-01 Thread sector119
Mike, is it possible to load "@property" as subquery? Maybe as 
ARRAY[path_item, path_item, ...]

воскресенье, 30 июня 2019 г., 20:26:42 UTC+3 пользователь Mike Bayer 
написал:
>
>
>
> On Sun, Jun 30, 2019, at 5:37 AM, sector119 wrote:
>
> Nice, thanks a lot, Mike, now it works as expected
>
>
> that's great.  the docs are not good here, there's not enough discussion 
> of "aliased()" , from_statement() and what they do, also I'm changing some 
> details of how they do their work for 1.4 in any case so documentation 
> efforts will be needed.
>
>
>
>
> @property
> *def *path(self):
> session = object_session(self)
>
> *def *get_locality_path_q(locality_id):
> parent_q = session.query(Locality).filter(Locality.id == 
> locality_id).cte(recursive=*True*)
>
> parents = aliased(parent_q)
>
> locality_alias = aliased(Locality)
>
> q = parent_q.union_all(
> session.query(locality_alias).join(parents, locality_alias.id == 
> parents.c.parent_id)
> )
>
> cte = aliased(Locality, q)
>
> *return *session.query(cte).order_by(cte.id)
>
> *return *get_locality_path_q(self.id)
>
>
> воскресенье, 30 июня 2019 г., 1:11:21 UTC+3 пользователь Mike Bayer 
> написал:
>
>
>
> On Sat, Jun 29, 2019, at 11:24 AM, sector119 wrote:
>
> Hello,
>
>
> I have Locality model with 'path' property to get path from 'root' of tree to 
> current item, everything works ok, but
>
> I can't get result as Locality instance list..
>
> When I use 
> *'*object_session(self).query(Locality).from_statement(q).order_by(Locality.id)'
>  I get 
>
> sqlalchemy.exc.ArgumentError: from_statement accepts text(), select(), and 
> union() objects only.
>
>
> How can I adopt results to Locality model?
>
>
>
> *class *Locality(Base):
> __tablename__ = 
> *'localities'*
> __table_args__ = {*'schema'*: SYSTEM_SCHEMA}
>
> id = Column(Integer, primary_key=*True*)
> parent_id = Column(Integer, ForeignKey(SYSTEM_SCHEMA + *'.localities.id 
> '*))
> name = Column(UnicodeText, nullable=*False*)
> type = Column(Integer, nullable=*False*)
>
> @property
> *def *path(self):
> *def *get_locality_path_q(locality_id):
> top_q = select([
> Locality.id,
> Locality.parent_id,
> Locality.name,
> Locality.type,
> ]).\
> where(Locality.id == locality_id).\
> cte(recursive=*True*)
>
> parents = aliased(top_q)
>
> locality_alias = aliased(Locality)
>
> q = top_q.union_all(
> select([
> locality_alias.id,
> locality_alias.parent_id,
> locality_alias.name,
> locality_alias.type
> ]).select_from(join(locality_alias, parents, 
> locality_alias.id == parents.c.parent_id))
> )
>
> 
> *# return object_session(self).query(q).order_by(q.c.id )* 
>*return 
> *object_session(self).query(Locality).from_statement(q).order_by(Locality.id)
>
> *return *get_locality_path_q(self.id)
>
>
> above, "q" is a CTE, not a SELECT, meaning it's something you can select 
> FROM, like a table.  Call select() to SELECT from it:
>
> return (
> object_session(self)
> .query(Locality)
> .from_statement(q.select().order_by(q.c.id))
> )
>
>
> additionally, from_statement() does not allow further changes to the 
> statement and the ORDER BY must be in terms of thing you are selecting 
> from, in this case q.c.id
>
> your code would be easier if you made use of top_q as a FROM object rather 
> than a statement:
>
> lcte = aliased(Locality, q)
>
> return (
> object_session(self)
> .query(lcte)
> .order_by(lcte.id)
> )
>
>
> There's not too many doc examples of how aliased() works with FROM clauses 
> but the basic idea is at:
>
>
> https://docs.sqlalchemy.org/en/13/orm/tutorial.html#selecting-entities-from-subqueries
>
> https://docs.sqlalchemy.org/en/13/orm/query.html#sqlalchemy.orm.aliased
>
> A little more on aliased() with CTE is written more for query.cte() which 
> you could also be using here:
>
>
> https://docs.sqlalchemy.org/en/13/orm/query.html?highlight=cte#sqlalchemy.orm.query.Query.cte
>
>
>
>
>
>
> --
> SQLAlchemy - 
> The Python SQL Toolkit and Object Relational Mapper
>  
> http://www.sqlalchemy.org/
>  
> To post example code, please provide an MCVE: Minimal, Complete, and 
> Verifiable Example. See http://stackoverflow.com/help/mcve for a full 
> description.
> --- 
> 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 sqlal...@googlegroups.com.
> To post to th

Re: [sqlalchemy] Grouping related Columns in table definitions and Python object reconstruction

2019-07-01 Thread Mike Bayer


On Mon, Jul 1, 2019, at 5:56 AM, gamcil wrote:
> Hi,
> 
> I'm new to SQLAlchemy/ORMs - previously I had just been interacting directly 
> with the builtin SQLite driver and had built up my own mess of a mapping 
> system.
> 
> 1) I'm starting to convert some classes in a Python package to SQLAlchemy 
> models. . A paired down example of what I'm doing:
> 
> class Gene:
> def __init__(self, identifiers, features):
> self.identifiers = identifiers
> ` self.features = features``
> 
> gene = Gene(identifiers={'locus': 'GENE_0001', 'protein_id': 'PROT_0001'},
>  features={'mRNA': '1..300,400..500', 'CDS': '1..300,400..500'})`
> 
> In reality, I'm dealing with many attributes that are all related to the Gene 
> and stored in the same table, which is why to simplify the interface of my 
> classes, I grouped related instance attributes in dictionaries. For example, 
> the location of the mRNA feature of this Gene can then be accessed by 
> gene.features['mRNA']. Each value in the dictionary refers to a unique 
> database column.
> 
> However, when creating SQLAlchemy models, it's required to explicitly map 
> Columns directly as class attributes, e.g.:
> `class Gene:
>  id = Column(Integer, primary_key=True)
>  locus = Column(String)`
> ` protein_id = Column(String)
>  mRNA = Column(String)
>  CDS = Column(String)`
> 
> Is there a simple way to provide the dictionary functionality? Something like:
> `class Gene:
>  id = Column(Integer, primary_key=True)
>  identifiers = {
>  'locus': Column(String),`
> ` 'protein': Column(String)
>  }
>  ...```
> Where a change in Gene.identifiers['locus'] would then result in an update to 
> the 'locus' column. I saw MutableDict in the documentation but it seems 
> non-trivial to implement for something like this.
> Is this even a good idea?

I'm going to focus on #1 here, so, this kind of thing is pretty straightforward 
in Python, throughout SQLAlchemy's examples / extensions there are all kinds of 
"magic dictionary" kinds of examples going on, and this one is...another one :) 
To give a high level for this kind of thing, as far as setting up mappings / 
tables, that can always be done "magically", that is, you can write event hooks 
to read any kind of configuration you want that will set up the mapper() / 
Table with whatever columns are needed. For the "get / set" a dictionary part, 
we always use a Python descriptor for that kind of thing, so in this case I'd 
likely be building a custom kind of descriptor class that also serves as the 
information to generate the table definition. Finally, you want the dictionary 
to be "live", that is, not get /set of the whole dictionary, you want changes 
in the dictionary to affect the model. So for the last part, yes there needs to 
be a custom dictionary object of some kind. It would use the same Python 
techniques as MutableDict but it likely would be specific to the case here, and 
overall this case is not that "hard". In this case I would create a Python 
object that is not in fact a "dict", but just an object that has as dict-like 
interface, e.g. __getitem__(), __setitem__(), keys(), etc., and then it just 
uses the object that it comes from as the source of data, like:


class GeneView:
 def __init__(self, gene, attrs):
 self.gene = gene
 self.attrs = attrs

 def keys(self):
 return iter(self.attrs)

 def items(self):
 return ((attr, getattr(self.parent, attr)) for attr in self.attrs)

 def __getitem__(self, key):
 return getattr(self.parent, key) # maybe you want to check self.attrs too

 def __setitem__(self, key, value):
 setattr(self.parent, key, value) # mutability


The above thing comes from a descriptor, a hardcoded version would look like:

class Gene(...):
 @property
 def identifiers(self):
 return GeneView(self, ['locus', 'protein'])


I'd then want to make the above "hardcoded" pattern more generic making a 
custom descriptor class 
(https://docs.python.org/3/howto/descriptor.html#descriptor-example) so that 
the mapping looks like:


class Gene(...):
 identifiers = gene_view_attr('locus', 'protein')


to map it, a simple event hook like before_mapper_configured (or declararive's 
declare_first / declare_last hooks: 
https://docs.sqlalchemy.org/en/13/orm/extensions/declarative/api.html?highlight=declare_last#declare-last)
 can be used, in this case it probably doesn't matter where in the mapping 
phase we are since columns can be added at any time:

from sqlalchemy import event

@event.listens_for(Base, "before_mapper_configured", propagate=True)
def setup_view_attrs(mapper, class_):
 for value in class_.__dict__.values():
 if isinstance(value, gene_view_attr): # look for our descriptor
 for attr in value.attrs:
 setattr(class_, attr, Column(attr, String)) # map columns


That's all you need for #1, let me know if you need more detail.




> 
> 2) Say for the example above I have created a plain Python Location class to 
> represent a location of a feature in self.features:
> class Location:
>  def __init__(self,

[sqlalchemy] Grouping related Columns in table definitions and Python object reconstruction

2019-07-01 Thread gamcil
Hi,

I'm new to SQLAlchemy/ORMs - previously I had just been interacting 
directly with the builtin SQLite driver and had built up my own mess of a 
mapping system.

1) I'm starting to convert some classes in a Python package to SQLAlchemy 
models. . A paired down example of what I'm doing:

class Gene:
def __init__(self, identifiers, features):
self.identifiers = identifiers
self.features = features

gene = Gene(identifiers={'locus': 'GENE_0001', 'protein_id': 'PROT_0001'},
features={'mRNA': '1..300,400..500', 'CDS': '1..300,400..500'})

In reality, I'm dealing with many attributes that are all related to the 
Gene and stored in the same table, which is why to simplify the interface 
of my classes, I grouped related instance attributes in dictionaries. For 
example, the location of the mRNA feature of this Gene can then be accessed 
by gene.features['mRNA']. Each value in the dictionary refers to a unique 
database column.
 
However, when creating SQLAlchemy models, it's required to explicitly map 
Columns directly as class attributes, e.g.:
class Gene:
id = Column(Integer, primary_key=True)
locus = Column(String)
protein_id = Column(String)
mRNA = Column(String)
CDS = Column(String)

Is there a simple way to provide the dictionary functionality? Something 
like:
class Gene:
id = Column(Integer, primary_key=True)
identifiers = {
'locus': Column(String),
'protein': Column(String)
}
...
Where a change in Gene.identifiers['locus'] would then result in an update 
to the 'locus' column. I saw MutableDict in the documentation but it seems 
non-trivial to implement for something like this.
Is this even a good idea?

2) Say for the example above I have created a plain Python Location class 
to represent a location of a feature in self.features:
class Location:
def __init__(self, intervals, ...):
self.intervals = intervals
...

@classmethod
def from_flat(cls, flat):
# convert flat location to list of tuples
# e.g. [(1, 300), (400, 500)]

This class has a classmethod that takes the stored flat string (e.g. 
1..300,400..500) and converts it to a list of interval tuples, and has some 
extra logic in it for use in my applications. I understand I can use 
orm.reconstructor to instantiate these Location objects from the stored 
attributes upon object loading, i.e.
@reconstructor
def init_on_load(self):
self.mRNA = Location.from_flat(self.mRNA)
...

However, what I don't get is how I should be shuttling changes in these 
objects back to the database as updates.
Is there some way to map this (instantiate Location on loading; mutate 
Location; convert Location back to flat format to update the Gene 
attribute)? Do i need to write setter methods for each that directly set 
the underlying column attribute to the flat representation of the Location?

Any help is appreciated, cheers

-- 
SQLAlchemy - 
The Python SQL Toolkit and Object Relational Mapper

http://www.sqlalchemy.org/

To post example code, please provide an MCVE: Minimal, Complete, and Verifiable 
Example.  See  http://stackoverflow.com/help/mcve for a full description.
--- 
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 sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at https://groups.google.com/group/sqlalchemy.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/sqlalchemy/f3105bee-b6dc-489c-82e0-c6d36710a462%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.