Ако приемем модел на връзка много към много в sqlalchemy с mysql, как да актуализирате данни, като игнорирате дубликати?

Малко съм заседнал с sqlalchemy, опитвайки се да актуализирам някои данни.

Имам връзка много към много и едно към много. Първият е връзката между автор и възможните изписвания на името му. Второто свързва авторите с тяхната писмена литература. Един доклад може да има няколко автора и обратно.

Ако приемем автор „Питър Шоу“, който вече има 4 статии, съхранени и свързани с него в базата данни. Не, искам да „добавя“ нов набор от 6 листа за „Питър Шоу“. За съжаление 4 от 6-те документа вече се съхраняват в базата данни. Ето защо session.commit() води до дублирана грешка.

Има ли общ начин да се избегнат дублиращите се грешки и да се каже на sqlalchemy просто да запълни дупките, вместо да се оплаква от дубликатите? Нито документът на sqlalchemy, нито google можеха да ме просветят с ясен отговор/подход, така че всички предложения са добре оценени.

Това са моделите, с които тествам:

class NameSpelling(Base):
    __tablename__ = 'name_spellings'

    id = Column(Integer, primary_key=True)
    name = Column(String(255), nullable=False, unique=True, index=True)
    authors_id = Column(Integer, ForeignKey('authors.id'))

    def __init__(self, name=None):
        self.name = name

    def __repr__(self):
        return "NameSpelling(%r)" % (self.name)

class Author(Base):
    __tablename__ = 'authors'

    id = Column(Integer, primary_key=True)
    name = Column(String(255), nullable=True, unique=True, index=True)

    papers = relationship('Paper',
                          secondary=author_paper,
                          backref='authors')

    name_spellings = relationship(NameSpelling,
                                  order_by=NameSpelling.id,
                                  backref="author",
                                  cascade="all, delete, delete-orphan")

    def __init__(self, name=None):
        self.name = name

    def __repr__(self):
        return "Authors(%r, %r)" % (self.name_spellings, self.name)


class Paper(Base):
    __tablename__ = 'papers'

    id = Column(Integer, primary_key=True)
    title = Column(String(1500), nullable=False, index=True)
    url = Column(String(255), nullable=False, unique=True, index=True)
    date = Column(Date(), nullable=True)

    def __init__(self, title=None, url=None, date=None):
        self.title = title
        self.url = url
        self.date = date

    def __repr__(self):
        return "Paper(%r)" % (self.title)

person Aufwind    schedule 01.07.2011    source източник


Отговори (1)


Имам абсолютно същия проблем с проект SQLAlchemy. Това, което в крайна сметка направих (и което вероятно е лош начин за справяне с проблема), е да проверя колекциите за взаимоотношения, преди да добавя нов екземпляр към сесията и да заменя свързаните екземпляри с резултата от session.merge(), ако има такъв .

Изглежда донякъде така:

def add_instance_to_session(instance, session):
    '''
    Add instance to session, while checking for existing child instances in
    the relationship collection instance.child_list.
    '''
    def _merge_and_replace(child):
        with session.no_autoflush:
            merged_child = session.merge(child)
            if id(merged_child) != id(child):
                try:
                    session.expunge(child)
                except sqlalchemy.exc.InvalidRequestError:
                    # child wasn't in the session to begin with
                    pass
                return merged_child
            else:
                return child
    instance.child_list = map(_merge_and_replace, instance.child_list)
    session.add(instance)

Това изглежда работи за мен, но изглежда като доста лошо представяне, особено ако имате много деца. Може би има по-добър начин да се използва идиомата ON DUPLICATE KEY, която предлага mySQL, или подобни конструкции.

[редактиране] Частта session.expunge() вероятно е ненужна, ако се използва само горният метод за добавяне на екземпляри към сесия, тъй като децата не могат да бъдат в сесията в този момент. Поне според мен е така...

person dorian    schedule 30.03.2012