Hello everyone... I'm experiencing an strange behavior with an
instrumented list...

On one hand, I have a class called "BaseMedia" that is the base type
for Images, Videos... On the other hand I have a class "Schedule" that
is the base class for "schedulable" things in my system and then I
have something called "AttractLoop" that is an Schedulable thing with
a list of media (images or videos to be played). All these elements
inherit from a class that is not mapped to any specific table called
"BaseClass" (where the primary key is). The structure is:

   BaseClass
         |
   BaseMedia
  /           \
Image   Video

 BaseClass
       |
  Schedule
       |
AttractLoop

The classes are:


============ BaseClass.py (super base class for everything) ======
class BaseClass(object):
        _id = Column("id", Integer, primary_key=True, key="id")

        #[ . . . Implementation, getters/setters and other methods . . .]

        @declared_attr
        def id(cls):
                return synonym('_id', descriptor=property(cls.getId, cls.setId))



===== The BaseMedia, base element for Images, Videos... =======

class BaseMedia(BaseClass.BaseClass, declarativeBase):
        __tablename__ = "base_media"
        _polymorphicIdentity = Column("polymorphic_identity", String(20),
                                                key="polymorphicIdentity")
        __mapper_args__ = {
                'polymorphic_on': _polymorphicIdentity,
                'polymorphic_identity': None
        }
        
        _name = Column("name", String(50))
        _md5Hash = Column("md5_hash", LargeBinary(32), key="md5Hash")

        #[ . . . Implementation, getters/setters and other methods . . .]

        name = synonym('_name', descriptor=property(getName, setName))
        md5Hash = synonym('_md5Hash', descriptor=property(getMd5Hash, 
setMd5Hash))



====== The base for every schedulable element ===========

class Schedule(BaseClass.BaseClass, declarativeBase):
        __tablename__ = "schedules"
        _polymorphicIdentity = Column("polymorphic_identity", String(20),
                                        key="polymorphicIdentity")
        __mapper_args__ = {
                'polymorphic_on': _polymorphicIdentity,
                'polymorphic_identity': None
        }

        _name = Column("name", String(256))
        _startDate = Column("start_date", Date, key="startDate")
        _endDate = Column("end_date", Date, key="endDate")
        _startTime = Column("start_time", Time, key="startTime")
        _endTime = Column("end_time", Time, key="endTime")

        def __init__(self):
                """Initialize object"""
                super(Schedule, self).__init__()
                self.name = ""
                self.startDate = None
                self.startTime = None
                self.endDate = None
                self.endTime = None

        #[ . . . Implementation, getters/setters and other methods . . .]

        name = synonym('_name', descriptor=property(getName, setName))
        startDate = synonym('_startDate', descriptor=property(getStartDate,
                                setStartDate))
        endDate = synonym('_endDate', descriptor=property(getEndDate, 
setEndDate))
        startTime = synonym('_startTime', descriptor=property(getStartTime,
                                setStartTime))
        endTime = synonym('_endTime', descriptor=property(getEndTime, 
setEndTime))



====== The attract loop class (child of Schedule) =======

class AttractLoop(Schedule.Schedule):
        __tablename__ = "attract_loops"
        __mapper_args__ = {
                'polymorphic_identity': 'AttractLoop'
        }

        _id = Column("id", Integer, ForeignKey(Schedule.Schedule.id), 
primary_key=True)
        _mediaItems = relationship(BaseMedia.BaseMedia,
                secondary=Tables.intermediate_attract_loop_to_media,
                order_by=BaseMedia.BaseMedia.name,
                collection_class=list
        )

        def __init__(self):
                super(AttractLoop, self).__init__()
                self.mediaItems = list()

        def getMediaItems(self):
                return self._mediaItems

        def setMediaItems(self, mediaItems):
                if mediaItems:
                        self._mediaItems = list(mediaItems)
                else:
                        self._mediaItems = list()

        def addMediaItem(self, mediaItem):
                self.mediaItems.append(mediaItem)
                
        def addMediaItemById(self, mediaItemId):
                mediaItem = 
backlib.media.BaseMediaManager.BaseMediaManager.getById(int(mediaItemId))
                if mediaItem:
                        if mediaItem.validityCheck():
                                self.addMediaItem(mediaItem)
                        else:
                                raise TypeError("Media item with id %s didn't 
pass the validity
                                                check" % mediaItemId)
                else:
                        raise KeyError("Media Item with id %s not found" % 
mediaItem)

        mediaItems = synonym('_mediaItems',
                                descriptor=property(getMediaItems, 
setMediaItems))

The auxiliary (secondary) table to model the relationship looks like this:

intermediate_attract_loop_to_media = Table(
        "intermediate_attract_loop_to_media",
        metadata,
        Column("id", Integer, primary_key=True),
        Column("attract_loop_id", Integer, ForeignKey("schedules.id"),
                        key="attractLoopId"),
        Column("base_media_id", Integer, ForeignKey("base_media.id"),
                        key="baseMediaId")
)

At a certain point, I generate some canned data (some samples) of
AttractLoop, and I append three times the same "media" object (same
instance = same numeric id) to the mediaItems list of the sample
attractLoop created.

Right before saving the sample attractLoop in the database, I can
check the length of its "mediaItems" field, and I properly get a
length of 3. If I show the ids of the media in that list, I properly
get:
>> Sample AttractLoop with id 4 has 3 mediaItems ([2L, 2L, 2L])

If I query the intermediate table, it properly shows:
+----+-----------------+---------------+
| id | attract_loop_id | base_media_id |
+----+-----------------+---------------+
|  1 |               4 |             2 |
|  2 |               4 |             2 |
|  3 |               4 |             2 |
|  4 |               3 |             1 |
|  5 |               3 |             1 |
|  6 |               3 |             1 |
|  7 |               2 |             2 |
|  8 |               2 |             2 |
|  9 |               2 |             2 |
| 10 |               1 |             1 |
| 11 |               1 |             1 |
| 12 |               1 |             1 |
+----+-----------------+---------------+

But if right after merging and flushing that sample I query it again:

query = session.query(AttractLoop.AttractLoop)
for attractLoop in query:
        print("Got attract loop %d with %d media thingies (type is %s).
                Content: %s\n" % (attractLoop.id, len(attractLoop.mediaItems),
                type(attractLoop.mediaItems), list(mediaThingy.id for 
mediaThingy in
                attractLoop.mediaItems)))


Got attract loop 1 with 1 media thingies (type is <class
'sqlalchemy.orm.collections.InstrumentedList'>). Content: [1L]

Got attract loop 2 with 1 media thingies (type is <class
'sqlalchemy.orm.collections.InstrumentedList'>). Content: [2L]

Got attract loop 3 with 1 media thingies (type is <class
'sqlalchemy.orm.collections.InstrumentedList'>). Content: [1L]

Got attract loop 4 with 1 media thingies (type is <class
'sqlalchemy.orm.collections.InstrumentedList'>). Content: [2L]

And I have no idea of why.

Sorry for the huge email, but I wanted to explain the problem in
detail, because I have no clue of why this is happening.

Thank you in advance

-- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to sqlalchemy@googlegroups.com.
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.

Reply via email to