SQLAlchemy如何在旧值[1, 2]和新值[2, 3]时更新子项
我有两个模型:
class Person(Model):
id
name
skills = relationship(Skill)
class Skill(Model):
id
skill
person_id
一开始,比如说:
jack = Person(name='jack')
jack.skills = [Skill(s) for s in ['python', 'ruby']]
jack.save()
然后,有一天,杰克失去了他的技能“ruby”,但获得了“swift”,所以他的技能变成了 ['python', 'swift']。
我现在更新技能的方式是:
- 先找出现有的技能,我得到
old = ['python', 'ruby']
- 然后获取新的技能列表
new = ['python', 'swift']
- 把旧的和新的技能都转成集合
set(old), set(new)
- 用
unchanged = old.intersection(new)
找出没有变化的技能 - 把每个在
set(new - unchanged)
中的技能加进去 - 把每个在
set(old-unchanged)
中的技能删掉
有没有更简单的方法来做到这一点呢?
1 个回答
0
在关系中使用 collection_class=set
,这样可以把它当作一个集合来处理,而不是一个列表。
下面是一个关于如何将人和技能关联起来的示例。这是一个多对多的关系,也就是说,每个技能可以关联多个不同的人,而不是每个技能只关联一个人。这个关系通过一个叫做 person_skill 的表来实现。这里的关系集合是一个 set
,而 Skill 类里有一个 __hash__
函数,这样同名的技能会被认为是相同的。
from sqlalchemy import create_engine, Column, Integer, String, ForeignKey, Table
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship
engine = create_engine('sqlite:///:memory:', echo=True)
Base = declarative_base(bind=engine)
Session = sessionmaker(bind=engine)
session = Session()
class Person(Base):
__tablename__ = 'person'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False, unique=True)
# many-to-many relation, as a set
skills = relationship('Skill', 'person_skill', collection_class=set)
class Skill(Base):
__tablename__ = 'skill'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False, unique=True)
def __hash__(self):
# so that the set collection will handle duplicate entries
return hash((self.__class__, self.name))
# many-to-many table, relate a person to a skill
person_skill = Table(
'person_skill', Base.metadata,
Column('person_id', Integer, ForeignKey(Person.id), primary_key=True),
Column('skill_id', Integer, ForeignKey(Skill.id), primary_key=True)
)
# create the tables
Base.metadata.create_all()
# populate some skills and people
s1 = Skill(name='python')
s2 = Skill(name='sqlalchemy')
s3 = Skill(name='questions')
s4 = Skill(name='ruby')
p1 = Person(name='davidism', skills={s1, s2, s4})
p2 = Person(name='user2653947', skills={s3})
session.add_all([p1, p2])
session.commit()
# change some skills on people
p1.skills.discard(s4)
p2.skills.add(s2)
session.commit()
这并不是一个完整的解决方案。比如,你可以参考 这个回答中展示的独特对象模式,确保你创建的技能不会重复。