OK, just three tables, then just three mapped classes, that's it.  The rest of 
the stuff you're trying to do has to be done using standard Python techniques; 
classes that implement special __getitem__() / __setitem__(), etc.   Here's a 
start, does just the "get" side so far:

from sqlalchemy import Integer, ForeignKey, create_engine, String, Column
from sqlalchemy.orm import Session, relationship
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()


class Group(Base):
    __tablename__ = 'group'

    id = Column(Integer, primary_key=True)
    name = Column(String)


class GroupGetter(object):
    def __init__(self, entity):
        self.entity = entity

    def __getitem__(self, key):
        # TODO: probably want to cache this
        return GroupItemGetter(self, key)


class GroupItemGetter(object):
    def __init__(self, groupgetter, groupname):
        self.entity = groupgetter.entity
        self.groupname = groupname
        self.data = dict(
            (group_entity.key, group_entity.value)
            for group_entity in self.entity._group_entities
            if group_entity.group.name == self.groupname
        )

    def __getitem__(self, key):
        return self.data[key]

    def __setitem__(self, key, value):
        raise NotImplementedError("an exercise")


class Entity(Base):
    __tablename__ = 'entity'

    id = Column(Integer, primary_key=True)
    name = Column(String)

    _group_entities = relationship("GroupEntity")

    @property
    def groups(self):
        # TODO: probably want to cache this
        return GroupGetter(self)


class GroupEntity(Base):
    __tablename__ = 'group_entity'

    id = Column(Integer, primary_key=True)
    entity_id = Column(ForeignKey('entity.id'))
    group_id = Column(ForeignKey('group.id'))
    key = Column(String)
    value = Column(String)

    group = relationship("Group")

e = create_engine("sqlite://", echo=True)
Base.metadata.create_all(e)


sess = Session(e)

ent = Entity(name="some customer")
g1, g2 = Group(name='g1'), Group(name='g2')

# setter side is raw so far
ent._group_entities.append(GroupEntity(group=g1, key='color', value='red'))
ent._group_entities.append(GroupEntity(group=g2, key='color', value='polkadot'))

sess.add(ent)
sess.commit()

# but here's the getter side
print ent.groups['g1']['color']




On Sep 10, 2014, at 8:08 PM, Anton <anschat...@gmail.com> wrote:

> I already have models for group, entity and group_entity. Now I am trying to 
> build a model that would represent an entity for a group with all the custom 
> attributes.
> 
> On Wednesday, September 10, 2014 5:06:36 PM UTC-7, Anton wrote:
> Mike, the tables are already there, I am trying to create models mapped on 
> the tables so it is easier to work with.
> Tables design looks like:
> 
> CREATE TABLE group (
>     id integer NOT NULL,
>     name character varying
> );
> 
> 
> CREATE TABLE entity (
>     id integer NOT NULL,
>     name character varying
> );
> 
> CREATE TABLE group_entity (
>     id integer NOT NULL,
>     entity_id integer,
>     key character varying,
>     value text,
>     group_id integer NOT NULL
> );
> 
> Best,
> Anton.
> 
> 
> 
> 
> 
> On Wednesday, September 10, 2014 4:51:54 PM UTC-7, Michael Bayer wrote:
> hi Anton -
> 
> if you can show me correct table designs, e.g. as simple CREATE TABLE 
> statements, that definitely do the thing you need, I can show you how to map 
> to it.  So far it's not clear if you're looking for schema design via 
> SQLAlchemy's API.
> 
> - mike
> 
> 
> On Sep 10, 2014, at 6:36 PM, Anton <ansch...@gmail.com> wrote:
> 
>> Hi Mike,
>> 
>> I have two models that I did not provide in the example: Entity and Group.
>> Every group can assign some customer attributes to an entity, this is 
>> reflected in GroupEntityAttributes.
>> Let's say GroupEntityAttributes(group_id=1, entity_id=1, key="color", 
>> value="red") would mean that Group 1 assigned a customer attribute "color" 
>> with a value "red" to 
>> the entity with id 1. So when I want to load an entity for a group I want to 
>> load the entity object plus all the custom attributes assigned to the entity 
>> but the group.
>> Also I need some mechanism mark an entity as loadable for the group, for 
>> this I am using a record like 
>> GroupEntityAttributes(group_id=1, entity_id=1, key="_has_connection", 
>> value=Null), so key "_has_connection" has a special meaning. If it presents, 
>> then the entity should be loaded for the group with all other custom 
>> attributes, otherwise no. For this purpose I created a model GroupEntity.
>> 
>> So this way GroupEntity.query.filter_by(group_id=1).all() will give a list 
>> with all entities loadable fir the Group 1. Every item of the list will have 
>> an association to Group object and Entity object, I want others property to 
>> represent a list of customer attributes, that I would like to implement as 
>> described here 
>> (https://bitbucket.org/zzzeek/sqlalchemy/src/374173e89d4e21a75bfabd8a655d17c247b6f1fc/examples/vertical/dictlike.py?at=master).
>>  For this I need to have a self referencing relationship to 
>> GroupEntityAttributes, which appears to be self-referencing because two 
>> models use the same table.
>> 
>> SimpleModel is just a mixin that I use in every model I create to make sure 
>> that they all have a unique id.
>> 
>> Thanks,
>> Anton.
>> 
>> 
>> On Wednesday, September 10, 2014 3:00:39 PM UTC-7, Michael Bayer wrote:
>> 
>> On Sep 10, 2014, at 5:11 PM, Anton <ansch...@gmail.com> wrote:
>> 
>>> So I am trying to do the following:
>>> class SimpleModel(object):
>>>     __table_args__ = {'extend_existing': True}
>>> 
>>>     id = db.Column(db.Integer, primary_key=True)
>>> 
>>> class GroupEntityAttributes(SimpleModel, db.Model):
>>>     __table_args__ = {'extend_existing': True}
>>>     __tablename__ = 'group_entity'
>>> 
>>>     #Table Columns
>>>     id = db.Column(db.Integer, primary_key=True)
>>>     group_id = db.Column(db.Integer, db.ForeignKey('groups.id'), 
>>> nullable=False)
>>>     entity_id = db.Column(db.Integer, db.ForeignKey('entities.id'), 
>>> nullable=False)
>>>     key = db.Column(db.String)
>>>     value = db.Column(db.Text)
>>> 
>>>     #Relationships
>>>     entity = db.relationship('Entity', lazy='joined')
>>>     group = db.relationship('Group')
>>> 
>>> class GroupEntityAttributesPoly(GroupEntityAttributes):
>>>     __mapper_args__ = {'polymorphic_on': GroupEntityAttributes.key}
>>> 
>>> class GroupEntity(GroupEntityAttributesPoly):
>>>     __mapper_args__ = {'polymorphic_identity': '_has_connection'}
>>> 
>>>     others = db.relationship(
>>>         'GroupEntityAttributes',
>>>         primaryjoin=and_(
>>>             remote(foreign(GroupEntityAttributes.group_id)) == 
>>> GroupEntityAttributes.group_id,
>>>             remote(foreign(GroupEntityAttributes.entity_id)) == 
>>> GroupEntityAttributes.entity_id,
>>>         )    
>>>     )
>>> 
>>> 
>> 
>> What's illustrated here doesn't really make sense.  If 
>> GroupEntityAttributes.group_id referred to "groups.id", then a relationship 
>> extending from GroupEntity would need to refer to whatever class is mapped 
>> to the "entities" table, which I don't see here.  It looks like you're 
>> taking foreign key columns on the "group_entity" table and somehow pointing 
>> it to itself in that primaryjoin which doesn't make sense.   GroupEntity 
>> would need to be mapped to either "entities" or "groups" in order to refer 
>> to GroupEntityAttributes using that pattern and this sort of looks like the 
>> association object pattern, see 
>> http://docs.sqlalchemy.org/en/rel_0_9/orm/relationships.html#association-object
>>  for an example of what that looks like.
>> 
>> Other things that you probably don't want:
>> 
>> 1. polymorphic_on on a subclass, that should always go on the mapped class 
>> at the bottom of the hierarchy, see 
>> http://docs.sqlalchemy.org/en/rel_0_9/orm/inheritance.html#joined-table-inheritance
>>  for basic examples
>> 
>> 2. extend_existing, this is usually only relevant when you're using 
>> reflection, otherwise it probably means you're setting things up incorrectly 
>> 
>> 
>> 
>> 
>> 
>> 
>> 
>> 
>> 
>> 
>>> When I try to load the model GroupEntity I get an error:
>>> ArgumentError: Can't determine relationship direction for relationship 
>>> 'GroupEntity.others' - foreign key columns within the join condition are 
>>> present in both the parent and the child's mapped tables.  Ensure that only 
>>> those columns referring to a parent column are marked as foreign, either 
>>> via the foreign() annotation or via the foreign_keys argument.
>>> 
>>> I am not sure how to overcome this error. Would appreciate any help.
>>> 
>>> Best,
>>> Anton.
>>> 
>>> On Tuesday, September 9, 2014 1:10:40 PM UTC-7, Anton wrote:
>>> Trying to use with multiple table mapping.
>>> 
>>> On Tuesday, September 9, 2014 1:03:25 PM UTC-7, Anton wrote:
>>> Hi, 
>>> 
>>> I need some help choosing the right pattern for the models I have. I have 
>>> the following tables:
>>> Entity (INT id, VARCHAR name)
>>> Group (INT id, VARCHAT name)
>>> GroupEntityAttributes(INT entity_id, INT group_id, VARCHAR key, VARCHAR 
>>> name)
>>> 
>>> Entity and Group models are pretty straight forward. But 
>>> GroupEntityAttributes represents Entity within some group with custom 
>>> attributes added, so it resembles this pattern ( 
>>> https://bitbucket.org/zzzeek/sqlalchemy/src/374173e89d4e21a75bfabd8a655d17c247b6f1fc/examples/vertical/dictlike.py?at=master
>>>  ), the only difference is that there is an additional foreign key.
>>> I wonder if there is any approach I can use to adapt vertical pattern for 
>>> my needs without modifying tables.
>>> 
>>> Best,
>>> Anton.
>>> 
>>> 
>>> 
>>> -- 
>>> 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+...@googlegroups.com.
>>> To post to this group, send email to sqlal...@googlegroups.com.
>>> 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 sqlalchemy+...@googlegroups.com.
>> To post to this group, send email to sqlal...@googlegroups.com.
>> 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 sqlalchemy+unsubscr...@googlegroups.com.
> To post to this group, send email to sqlalchemy@googlegroups.com.
> 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 sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.

Reply via email to