KageUrufu
KageUrufu

Reputation: 494

Using inspect to get the history of a model's relationship

I am able to use sqlalchemy's inspect function to find the history of a specific model, for any normal value. However, when I attempt to get the history for a relationship (or its related ForeignKey column), it only shows the new value, not the previous value.

Is there any quick way to get the previous value of a relationship like this, or do I need to use another function, or completely roll my own?

Basic code I am using looks like

class Person(db.Model):
  id = db.Column(db.Integer(), primary_key=True)
  name = db.Column(db.String(80))
  phone_id = db.Column(db.Integer(), db.ForeignKey("phone_number.id"))
  phone = db.relationship("PhoneNumber")
class PhoneNumber(db.Model):
  id = db.Column(db.Integer(), primary_key = True)
  type = db.Column(db.String(20))
  number = db.Column(db.String(11))

p = Person()
p.name = "Frank"
phone1 = PhoneNumber("Mobile", "1234567890") #__init__ function handles these args
phone2 = PhoneNumber("Home", "9876543210")
p.phone = phone1
db.session.add_all([p, phone1, phone2])
db.session.commit()
p.phone = phone2
history = db.inspect(p).attrs.get("phone").history
# history is sqlalchemy.orm.attributes.History(([<PhoneNumber('Home', '9876543210')>], (), ()))
# history.added is ([<PhoneNumber('Home', '9876543210')>])
# history.deleted is () but should be ([<PhoneNumber('Mobile', '1234567890')>])

Upvotes: 4

Views: 1663

Answers (1)

zzzeek
zzzeek

Reputation: 75117

the "old" value of a scalar attribute is usually not loaded from the database if not present already, as this is typically wasteful (in this case, session.commit() expires all attributes, so Person.phone is unloaded when the new value is assigned). Exceptions include when the column is a primary key column, or for a scalar relationship with something other than a simple many-to-one. This loading of the "old" value can be forced by using the "active_history" option on the attribute in question.

from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Person(Base):
    __tablename__ = 'person'
    id = Column(Integer(), primary_key=True)
    name = Column(String(80))
    phone_id = Column(Integer(), ForeignKey("phone_number.id"))
    phone = relationship("PhoneNumber", active_history=True)

class PhoneNumber(Base):
    __tablename__ = 'phone_number'
    id = Column(Integer(), primary_key=True)
    type = Column(String(20))
    number = Column(String(11))

e = create_engine("sqlite://", echo=True)
Base.metadata.create_all(e)

session = Session(e)

p = Person()
p.name = "Frank"
phone1 = PhoneNumber(type="Mobile", number="1234567890")
phone2 = PhoneNumber(type="Home", number="9876543210")
p.phone = phone1
session.add_all([p, phone1, phone2])
session.commit()
p.phone = phone2
history = inspect(p).attrs.get("phone").history

assert history == ([phone2], (), [phone1])

Upvotes: 5

Related Questions