On 7/6/15 3:13 PM, Ryan Holmes wrote:
I have tried this, but still cannot get it figured out. I keep getting exceptions such as " Could not determine join condition between parent/child tables on relationship...", etc. There are two things that seems to be hindering me:

- I'm using classical mappings. Many resources online assume declarative style, and I'm having trouble converting it. The use of classical mapping is due to how the project was started and is the convention used. To move to declarative style would be a rather large undertaking, though I guess it's possible.

classical mappings are more verbose but in many ways easier, because you can just refer to the Table object you need rather than playing lots of games with declarative's string-style config.


- I'm using a self-referential mapping, which also complicates things, since most resources I find have two distinct objects along with the association.

Well yes, self referential m2m is described at http://docs.sqlalchemy.org/en/rel_1_0/orm/join_conditions.html#self-referential-many-to-many-relationship. However, once we use an association object here, the relationship is actually not self-referential anymore, it becomes two pairs of o2m/m2o relationships.


Below is a proof concept.



from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.associationproxy import association_proxy


metadata = MetaData()

item_table = Table(
    "items", metadata,
    Column("ID", Integer, primary_key=True),
    Column("name", String, nullable=False),
    Column("timestamp", Integer, nullable=False),
    Column("projected", Boolean),
)

projectedItem_table = Table(
    "projectedItem", metadata,
    Column("sourceID", ForeignKey("items.ID"), primary_key=True),
    Column("destID", ForeignKey("items.ID"), primary_key=True),
    Column("enabled", Integer))


class Item(object):
    def __init__(self, name, timestamp):
        self.name = name
        self.timestamp = timestamp

    projectedItem = association_proxy(
        "dest_items", "source_item",
        creator=lambda source_item: ProjectedItem(source_item, None, False)
    )


class ProjectedItem(object):
    def __init__(self, source_item, dest_item, enabled):
        self.source_item = source_item
        source_item.projected = True
        self.dest_item = dest_item
        self.enabled = enabled


mapper(
    Item, item_table,
    properties={
        "dest_items": relationship(
            ProjectedItem,
            primaryjoin=projectedItem_table.c.destID == item_table.c.ID,
            backref="dest_item"
        ),
        "source_items": relationship(
            ProjectedItem,
            primaryjoin=item_table.c.ID == projectedItem_table.c.sourceID,
            backref="source_item"
        )
    }
)

mapper(ProjectedItem, projectedItem_table)


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


s = Session(e)
i1 = Item("i1", 1)
i2 = Item("i2", 2)
i3 = Item("i3", 3)
i4 = Item("i4", 4)
i5 = Item("i5", 5)

i1.projectedItem.append(i2)
i1.projectedItem.append(i3)


i3.projectedItem.append(i4)

i3.dest_items[0].enabled = True


i4.dest_items.append(ProjectedItem(i5, i4, True))

s.add_all([i1, i2, i3, i4, i5])
s.commit()


i3 = s.query(Item).filter_by(name='i3').first()
for proj_item in i3.dest_items:
    print(proj_item.source_item, proj_item.enabled)





On Monday, July 6, 2015 at 1:14:29 PM UTC-4, Michael Bayer wrote:



    On 7/6/15 12:06 PM, Ryan Holmes wrote:
    I have an interesting problem that I haven't been able to solve
    for quite some time. I keep finding information about association
    proxies and the like, but nothing that really helps (or maybe I'm
    implementing it incorrectly).

    Let's start with this:

    |
    classHandledProjectedItemList(list):
    defappend(self,proj):
                proj.projected =True
                list.append(self,proj)


        item_table =Table("items",saveddata_meta,
    Column("ID",Integer,primary_key =True),
    Column("itemID",Integer,nullable =False,index =True),
    Column("name",String,nullable =False),
    Column("timestamp",Integer,nullable =False),
    )

        projectedItem_table =Table("projectedItem",saveddata_meta,
    Column("sourceID",ForeignKey("item.ID"),primary_key =True),
    Column("destID",ForeignKey("item.ID"),primary_key =True),
    Column("enabled",Integer))

        mapper(Item,item_table,
            properties ={"projectedItem":relation(Item,
                            primaryjoin =projectedItem_table.c.destID
    ==item_table.c.ID,
                            secondaryjoin =item_table.c.ID
    ==projectedItem_table.c.sourceID,
                            secondary =projectedItem_table,
                            collection_class =HandledProjectedItemList)
    })
    |


    I have two tables: a `item` table, and a `projectedItem` table.
    The `item` table is the main one, and contains information on
    items. The projected items table is a self-referential
    many-to-many relationship to the items table, where each item may
    have a collection of other items attached to it. We use a custom
    collection class to load this relationship so that we can modify
    a special attribute in the Item objects (if they are loaded via
    this relationship, they have their `projected` attribute set to
    `True`).

    This works great. But I also want read / write access to that
    extra `enabled` column in the relationship table, while
    maintaining the current functionality of loading the projected
    items into this custom collection class. I haven't found any
    information on that that helps, or like I said before, maybe I'm
    just not using it correctly.
    you would make a new class ProjectedItem and map it to the
    projectedItem_table.  This is the association object pattern (get
    that to work first, without the "proxy" part), illustrated at
    
http://docs.sqlalchemy.org/en/rel_1_0/orm/basic_relationships.html#association-object
    
<http://docs.sqlalchemy.org/en/rel_1_0/orm/basic_relationships.html#association-object>.
    Once that works, and all that's left is the inconvenience of
    navigating from Item->projecteditems->item, then you can apply the
    association proxy pattern to convert those two hops into just one
    (http://docs.sqlalchemy.org/en/rel_1_0/orm/extensions/associationproxy.html
    
<http://docs.sqlalchemy.org/en/rel_1_0/orm/extensions/associationproxy.html>).



-- 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] <javascript:>.
    To post to this group, send email to [email protected]
    <javascript:>.
    Visit this group at http://groups.google.com/group/sqlalchemy
    <http://groups.google.com/group/sqlalchemy>.
    For more options, visit https://groups.google.com/d/optout
    <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] <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.

Reply via email to