为什么SQLAlchemy在删除对象后不更新关系?

时间:2018-02-09 09:32:42

标签: python orm sqlalchemy

问题

在同一会话中,删除对象后,会话中包含或指向此已删除对象的其他对象的关系属性不会更新。简而言之:

sesion.add(a, b)
a.parent = b
print(a.parent)  # b
session.delete(b)
print(a.parent)  # b

完整,可重复的例子如下。

材料

我已阅读详细的SQLA文档,包括:

  • 'deleting from collections'上的误导性部分。误导因为它让你认为删除后缺少更新仅限于集合。下面的例子显示它不是。
  • cascades上的部分,尤其是'delete-orphan'。这是我正在寻找的东西,因为我不想删除所有关系以删除相关对象。

潜在解决方案

可以手动设置单个对象甚至单个对象属性,以使用session.expire()重新加载。我认为ORM负责这项工作: - )

问题

  • 我错过了一个简单的解决方案吗?
  • 我是否错过了文档中明确写入和解释的位置? (我花了很长时间来缩小问题范围)
  • 有没有理由说SQLAlchemy没有/不能标记更新绑定到被删除对象的关系?

代码示例

SQLA设置

from sqlalchemy import create_engine, Table, Column, Integer, String, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, backref, sessionmaker

engine = create_engine('sqlite:///:memory:', echo=True)
Base = declarative_base()
Session = sessionmaker(bind=engine)
session = Session()

模型定义

class Country(Base):
    __tablename__ = 'countries'

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

    def __repr__(self):
        return '<Country {} [{}]>'.format(self.name, self.id)


class Capital(Base):
    __tablename__ = 'capitals'

    id = Column(Integer(), primary_key=True)
    name = Column(String(255))
    country_id = Column(Integer(), ForeignKey(Country.id), unique=True)
    country = relationship('Country', backref=backref('capital', uselist=False))

    def __repr__(self):
        return '<Capital {} [{}]>'.format(self.name, self.id)


Base.metadata.create_all(engine)

测试

# Creating both objects
us = Country(name="USA")
ny = Capital(name="NYC")
session.add(us)
session.add(ny)
session.commit()

print("\n### 1. Creating the relation:")
# Loading relations:
print("## us.capital: ", us.capital)
print("## ny.country: ", ny.country)
# Creating the relation:
us.capital = ny
# Checking that it's upated on the other side:
print("## ny.country (after rel creation): ", ny.country)
# Saving
session.commit()


print("\n### 2. Deleting relation:")
# Loading relations:
print("## us.capital: ", us.capital)
print("## ny.country: ", ny.country)
# Deleting one object of the relation:
us.capital = None
# The relation from the other side are updated accordingly
print("## ny.country (after rel deletion): ", ny.country)
# Rolling back
session.rollback()


print("\n### 3. Deleting one object:")
# Loading relations:
print("## us.capital: ", us.capital)
print("## ny.country: ", ny.country)
# Deleting one object of the relation:
session.delete(us)
# The relations are not updated!
print("## ny.country (after deletion of us): ", ny.country)
# Flushing doesn't change anything (undersantably so)
session.flush()
print("## ny.country (+ flush): ", ny.country)
# Expiring manually
session.expire(ny, ['country'])
# Looks okay
print("## ny.country (+ expire): ", ny.country)
# Rolling back
session.rollback()


print("\n### 4. Deleting the other object:")
# Loading relations:
print("## us.capital: ", us.capital)
print("## ny.country: ", ny.country)
# Deleting one object of the relation:
session.delete(ny)
# The relations are not updated!
print("## us.capital (after deletion of ny): ", us.capital)
# Flushing doesn't change anything (undersantably so)
session.flush()
print("## us.capital (+ flush): ", us.capital)
# Expiring manually
session.expire(us, ['capital'])
# Looks okay
print("## us.capital (+ expire): ", us.capital)
# Rolling back
session.rollback()

结果

### 1. Creating the relation:
## us.capital:  None
## ny.country:  None
## ny.country (after rel creation):  <Country USA [1]>

### 2. Deleting relation:
## us.capital:  <Capital NYC [1]>
## ny.country:  <Country USA [1]>
## ny.country (after rel deletion):  None

### 3. Deleting one object:
## us.capital:  <Capital NYC [1]>
## ny.country:  <Country USA [1]>
## ny.country (after deletion of us):  <Country USA [1]>
## ny.country (+ flush):  <Country USA [1]>
## ny.country (+ expire):  None

### 4. Deleting the other object:
## us.capital:  <Capital NYC [1]>
## ny.country:  <Country USA [1]>
## us.capital (after deletion of ny):  <Capital NYC [1]>
## us.capital (+ flush):  <Capital NYC [1]>
## us.capital (+ expire):  None

0 个答案:

没有答案