【问题标题】:Delete whole hierarchy with sqlalchemy polymorphic relationship使用 sqlalchemy 多态关系删除整个层次结构
【发布时间】:2015-04-16 17:59:08
【问题描述】:

我想删除 sqlalchemy 中具有多态关系的表中的一些元素。这是模型:

class Employee(Base):
  __tablename__ = 'employee'
  id = Column(Integer, primary_key=True)
  name = Column(String(50))
  type = Column(String(50))

  __mapper_args__ = {
    'polymorphic_identity':'employee',
    'polymorphic_on':type
  }

class Engineer(Employee):
  __tablename__ = 'engineer'
  id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
  engineer_name = Column(String(30))

  __mapper_args__ = {
    'polymorphic_identity':'engineer',
  }

这是我删除它的方法:

e = Engineer();
e.name = "John";
e.engineer_name = "Doe";
DBSession.add(e);

q = session.query(Engineer).filter(Employee.name == "John")

q.delete(False)

我收到以下错误,这是一个错误还是我做错了?

sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such
column: employee.name [SQL: u'DELETE FROM engineer WHERE employee.name
= ?'] [parameters: ('John',)]

我希望 sqlalchemy 删除工程师和雇员表中的条目。

【问题讨论】:

  • 不应该是q = session.query(Engineer).filter(Engineer.name == "John")吗? (而不是Employee.name

标签: python sqlalchemy polymorphism polymorphic-associations


【解决方案1】:

首先你应该定义这个关系的删除行为:

id = Column(Integer, ForeignKey('employee.id', ondelete='CASCADE'), primary_key=True)

然后,使用 ORM,您可以通过循环删除所有名为“John”的工程师:

eng_list = session.query(Engineer).filter(Employee.name == "John").all()

for eng in eng_list:
   session.delete(eng)
session.commit()

这将从EmployeeEngineer 表中删除记录。

更新:对错误信息的评论:

sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such 
column: employee.name [SQL: u'DELETE FROM engineer WHERE employee.name
= ?'] [parameters: ('John',)]

您尝试从工程师中删除并加入 Employee(以访问 Employee.name 字段)。但是 sqlalchemy 发送到后端的查询中缺少此连接。

我认为 SQLite 不支持使用连接删除。或许您可以尝试针对不同的后端运行 session.query(Engineer).filter(Employee.name == "John").delete(),然后 sqlalchemy 可能 能够发出正确的 SQL 语句。不过我没试过。

更新 2: 在尊重外键约束(并且 onupdate 约束已设置为 cascade)的后端上,删除父行中的行就足够了,并且子中的链接行将被自动删除。

我使用 MySQL 和 Postgresql 后端尝试了这个示例,以下查询从两个表中删除了该行 (employee & engineer):

session.query(Employee).filter(Employee.name=='John').delete()

由于某种原因,在 Sqlite 上,这只会删除来自 employee 的记录。

【讨论】:

    【解决方案2】:

    因为不直接支持加入 DELETE,所以我发现一个简单的解决方法是使用您的正常加入查询来选择要删除的 id,然后将这些 id 传递给单独的 DELETE 查询。

    一个小烦恼是,由于您返回的 id 是整数,因此如果您尝试将这些 id(技术上是元组数组)直接传递给 DELETE 查询,您可能会遇到this error。一个简单的到字符串的中间转换可以解决这个问题。

    所以大家在一起:

    ids_to_delete = session.query(Engineer.id). \
        filter(Employee.name == "John"). \
        all()
    
    # Convert the resulting int tuples to simple strings:
    id_strings = [str(id_[0]) for id_ in ids_to_delete]
    
    session.query(Engineer). \
        filter(Engineer.id.in_(id_strings)). \
        delete(synchronize_session=False)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-05-03
      • 1970-01-01
      • 1970-01-01
      • 2021-11-23
      • 1970-01-01
      • 2021-06-09
      • 2013-12-06
      相关资源
      最近更新 更多