假设sqlalchemy中的多对多关系模型与mysql,如何更新数据忽略重复?

时间:2011-07-01 05:53:39

标签: python many-to-many sqlalchemy duplicates

我对sqlalchemy试图更新一些数据感到有点困惑。

我有多对多一对多关系。第一个是作者与他名字的可能拼写之间的关系。第二个是将作者与他们的书面文献联系起来。论文可能有几位作者,反之亦然。

假设作者“Peter Shaw”已经在数据库中存储了4篇论文并将其链接到了他。不,我想为“彼得肖”“添加”一套新的6篇论文。不幸的是,6篇论文中有4篇已经存储在数据库中。这就是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)

1 个答案:

答案 0 :(得分:0)

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

这似乎对我有用,但是表现相当糟糕,特别是如果你有很多孩子。也许有更好的方法利用mySQL提供的ON DUPLICATE KEY习惯用法或类似的结构。

[edit]如果只使用上述方法向会话添加实例,则session.expunge()部分可能是不必要的,因为此时子节点不能在会话中。至少这就是我认为的......