【问题标题】:SQLAlchemy: create many-to-many and populate associationSQLAlchemy:创建多对多并填充关联
【发布时间】:2022-01-13 14:15:15
【问题描述】:

我的想法如下:

  • 有一个主表 (Documents),其中包含一些文本,例如博客文章。每个文档都有一个唯一的 ID text_id
  • 有一个辅助表 (Links) 存储这些帖子中出现的唯一 URL。每个网址都有一个唯一的 ID url_id
  • 这些由关联表 (Association) 绑定,该关联表将文本的 id 映射到域的 id。

我希望能够获取帖子,从中收集网址,然后:

  • Documents 中创建新记录
  • 如果它包含新的 url - 将它们添加到 Links 并通过 Association 与文档相关联
  • 如果文档包含已有的 url - 仅在新文档和这些文档之间创建关联。

对于初学者,我创建了三个类,例如here

class Association(Base):
    __tablename__ = 'association'
    text_id = Column('text_id', Integer, ForeignKey('left.text_id'), primary_key=True)
    url_id = Column('url_id', Integer, ForeignKey('right.url_id'), primary_key = True)
    child = relationship("Links", back_populates='parents')
    parent = relationship("Documents", back_populates='children')

class Documents(Base):
    __tablename__ = 'left'
    text_id = Column(Integer, primary_key=True, unique=True)
    text = Column(Text)
    children = relationship("Association", back_populates='parent')

class Links(Base):
    __tablename__ = 'right'
    url_id = Column(Integer, primary_key=True, autoincrement=True, unique=True)
    url = Column(Text, unique=True)
    parents = relationship('Association', back_populates = 'child')

Base.metadata.create_all(engine)

然后我正在尝试加载数据:

data = [
    {'id':1, 'text':'sometext', 'url':'facebook.com'},
    {'id':2, 'text':'sometext', 'url':'twitter.com'},
    {'id':3, 'text':'sometext', 'url':'twitter.com'}
]

for row in data:
    d = Document(text_id = row['id'])
    a = Association()
    a.child = Links(url = row['url'])
    d.children.append(a)
    session.add(d)
session.commit()

这会导致错误:

Traceback (most recent call last):
  File "/home/user/.pyenv/versions/3.7.12/envs/myenv/lib/python3.7/site-packages/IPython/core/interactiveshell.py", line 3444, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-13-325b1cd57576>", line 5, in <module>
    p.children.append(a)
  File "/home/user/.pyenv/versions/3.7.12/envs/myenv/lib/python3.7/site-packages/sqlalchemy/util/langhelpers.py", line 1240, in __getattr__
    return self._fallback_getattr(key)
  File "/home/user/.pyenv/versions/3.7.12/envs/myenv/lib/python3.7/site-packages/sqlalchemy/util/langhelpers.py", line 1214, in _fallback_getattr
    raise AttributeError(key)
AttributeError: append

我真的不明白为什么,因为我似乎按照官方文档的建议做了所有事情。

另一方面,即使这样可行,我怀疑通过 p.children.append(a) 附加一个已经存在的 url 可能会导致错误,因为它实际上会尝试创建一个副本,而 Links 不允许这样做。

如果重要的话,我正在使用 mySQL 和 MariaDB。

也许我为这项工作选择了错误的工具 - 如果您能提出替代方案,我将不胜感激。

UPD:我无法插入,因为我使用 automap_base() 而不是 declarative_base() 实例化了一个基。现在我可以追加,但是,重复的条目确实是个问题:

sqlalchemy.exc.IntegrityError: (pymysql.err.IntegrityError) (1062, "Duplicate entry 'twitter.com' for key 'url'")
[SQL: INSERT INTO `right` (url) VALUES (%(url)s)]
[parameters: {'url': 'twitter.com'}]
(Background on this error at: https://sqlalche.me/e/14/gkpj)

【问题讨论】:

    标签: python sql sqlalchemy


    【解决方案1】:

    首先,如果您使用正确的域名而不是:rightleftchildchildren,将更容易调试。我知道这是文档的副本,但是文档是通用的,而您的案例是特定的。您的代码将更具可读性。

    为避免重复,您应在插入该记录之前检查是否已存在(Documents 具有唯一的 text_idLinks 具有唯一的 url)。

    for row in data:
        d = session.query(Document).filter_by(text_id=row['id']).first()
        if not d:
            d = Document(text_id=row['id'])
        link = session.query(Links).filter_by(url=row['url']).first():
        if not link:
            link = Links(url=row['url'])
        a = Association(child=link)
        d.children.append(a)
        session.add(d)
        session.flush()
    session.commit()
    

    【讨论】:

    • 非常感谢您的解决方案!是的,下次我会更加注意命名:)
    猜你喜欢
    • 2019-10-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-07
    • 2015-03-16
    • 2016-06-13
    • 2019-01-12
    相关资源
    最近更新 更多