我可以将同一个对象追加到SQLAlchemy中的InstrumentedList两次吗?

时间:2011-04-13 23:01:53

标签: python list sqlalchemy multiple-instances adjacency-list

我在SqlAlchemy 0.6.6中有一个非常简单的N:M关系。我有一个类“attractLoop”,可以包含一堆媒体(图像或视频)。我需要有一个列表,其中相同的媒体(比如图像)可以附加两次。关系如下:

媒体是基类,图像和视频将共享大部分属性。

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))
    _type = Column("type", String(50))
    _size = Column("size", Integer)
    _lastModified = Column("last_modified", DateTime, key="lastModified")
    _url = Column("url", String(512))
    _thumbnailFile = Column("thumbnail_file", String(512), key="thumbnailFile")
    _md5Hash = Column("md5_hash", LargeBinary(32), key="md5Hash")

然后是要使用这些“媒体”事物的班级:

class TestSqlAlchemyList(BaseClass.BaseClass, declarativeBase):
    __tablename__ = "tests"
    _mediaItems = relationship("BaseMedia",
                               secondary=intermediate_test_to_media,
                               primaryjoin="tests.c.id == intermediate_test_to_media.c.testId",
                               secondaryjoin="base_media.c.id == intermediate_test_to_media.c.baseMediaId",
                               collection_class=list,
                               uselist=True
                               )

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

    def getMediaItems(self):
        return self._mediaItems

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

    def addMediaItem(self, mediaItem):
        self.mediaItems.append(mediaItem)
        #log.debug("::addMediaItem > Added media item %s to %s. Now length is %d (contains: %s)" % (mediaItem.id, self.id, len(self.mediaItems), list(item.id for item in self.mediaItems)))

    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))

连接两个表的中间类:

intermediate_test_to_media = Table(
                                   "intermediate_test_to_media",
                                   Database.Base.metadata,
                                   Column("id", Integer, primary_key=True),
                                   Column("test_id", Integer, ForeignKey("tests.id"), key="testId"),
                                   Column("base_media_id", Integer, ForeignKey("base_media.id"), key="baseMediaId")
                                   )

当我将相同的Media对象(实例)两次附加到该TestSqlAlchemyList的一个实例时,它会正确附加两个,但是当我从数据库中检索TestSqlAlchemyList实例时,我只得到一个。它似乎表现得更像一套。

中间表具有正确的所有信息,因此插入似乎工作正常。当我没有收到我插入的所有项目时,是否尝试从数据库加载列表。

mysql> SELECT * FROM intermediate_test_to_media;
+----+---------+---------------+
| id | test_id | base_media_id |
+----+---------+---------------+
|  1 |       1 |             1 |
|  2 |       1 |             1 |
|  3 |       1 |             2 |
|  4 |       1 |             2 |
|  5 |       1 |             1 |
|  6 |       1 |             1 |
|  7 |       2 |             1 |
|  8 |       2 |             1 |
|  9 |       2 |             1 |
| 10 |       2 |             2 |
| 11 |       2 |             1 |
| 12 |       2 |             1 |

如您所见,id = 1的“test”实例应该具有媒体[1,1,2,2,1,1]。嗯,事实并非如此。当我从DB加载它时,它只有媒体[1,2]

我试图在关系中设置任何可能闻到的气味列表...... uselist,collection_class = list ...没什么......

您将看到类继承自BaseClass。这只是一个实际上没有映射到任何表的类,但包含一个数字字段(“id”),它将成为每个类的主键和一系列对我系统中其余类有用的其他方法(toJSON) ,toXML ......)。为了以防万一,我附上了它的摘录:

class BaseClass(object):
    _id = Column("id", Integer, primary_key=True, key="id")
    def __hash__(self):
        return int(self.id)

    def setId(self, id):
            try:
                    self._id = int(id)
            except TypeError:
                    self._id = None

    def getId(self):
        return self._id

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

如果有人能推动我,我会非常感激。谢谢。抱歉这个巨大的帖子......我真的不知道如何更好地解释。

1 个答案:

答案 0 :(得分:1)

我认为您想要的是文档中描述为“Extra Fields in Many-to-Many Relationships”。您可以存储单个关联并指定(作为链接对象模型的一部分)引用它的次数和/或在哪个位置(而不是在数据库foreach“link”中存储唯一行,在吸引流和媒体之间。 s)在最终列表中应该出现媒体。这是一个与你开始的地方不同的范例,所以它肯定需要一些重新编码,但我认为它解决了你的问题。您可能需要使用属性来重新定义如何从attactLoop添加或删除Media。