Можно ли дважды добавить один и тот же объект в InstrumentedList в SQLAlchemy? - PullRequest
1 голос
/ 14 апреля 2011

У меня довольно простые отношения N: M в SqlAlchemy 0.6.6. У меня есть класс «AttraLoop», который может содержать кучу медиа (изображения или видео). Мне нужно иметь список, в котором один и тот же медиа (скажем, изображение) может быть добавлен дважды. Соотношение следующее:

Носитель является базовым классом с большинством атрибутов, которыми будут делиться изображения и видео.

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

И промежуточный класс, чтобы связать обе таблицы:

* * 1010

Когда я добавляю один и тот же объект 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 должен иметь носитель [1, 1, 2, 2, 1, 1]. Ну, это не так. Когда я загружаю его из БД, он имеет только носитель [1, 2]

Я пытался установить любой параметр в отношениях, который мог бы пахнуть списком ... uselist, collection_class = list ... Nothing ...

Вы увидите, что классы наследуются от 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 Ответ

1 голос
/ 15 апреля 2011

Я думаю, что вы хотите описать в документации как " Дополнительные поля в отношениях" многие ко многим "".Вместо того, чтобы хранить уникальную строку в базе данных с каждой ссылкой, между AttraLoop и Media, вы должны хранить одну ассоциацию и указывать (как часть модели объекта ссылки), сколько раз на нее ссылаются и / или в каком месте (s) в окончательном списке СМИ должны появиться.Это парадигма, отличная от той, с которой вы начали, поэтому она, безусловно, потребует некоторой перекодировки, но я думаю, что она решает вашу проблему.Скорее всего, вам потребуется использовать свойство, чтобы переопределить способ добавления или удаления Media из attactLoop.

...