我可以将同一个对象两次添加到SQLAlchemy的InstrumentedList中吗?
我在使用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")
)
当我把同一个媒体对象(实例)添加两次到这个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]。但实际上并不是这样。当我从数据库加载时,它只包含媒体[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 个回答
我觉得你想要的内容在文档中被描述为“多对多关系中的额外字段”。与其在数据库中为每个“链接”存储一行独特的数据,像是在 attractLoop 和 Media 之间,你可以只存储一个关联,并在链接对象模型中指定这个关联被引用了多少次,或者在最终列表中媒体应该出现在什么位置。这种方法和你最开始的想法有些不同,所以肯定需要重新编写一些代码,但我觉得这样可以解决你的问题。你可能还需要使用一个属性来重新定义如何将 Media 添加到或移除出 attractLoop。