A fraught relationship with backref
While learning to code in Python a few days ago, backref and I had a standoff. We stared at each other for what felt like an eternity, attempting to make sense of each other. Well, I was attempting to make sense of it while it was just sitting there, but it felt mutual at the time. I knew I needed backref, I knew what its syntax should look like, and I even had functional code free of errors, but backref was still a mystery to me. It’s about time we got better acquainted.
What does backref even mean?
Put very (perhaps overly) simply, backref helps a class table to refer back to another class table when they’re joined by a ForeignKey.
But… what does that mean?
We are accustomed to seeing attributes within classes. For example:
engine = create_engine('sqlite:///bands.db')
Session = sessionmaker(bind=engine)
session = Session()
class Musician(Base):
__tablename__ = "musician"
id = Column(Integer(), primary_key=True)
name = Column(String())
musician1 = Musician(name = "Dave Grohl")
session.add(musician1)
session.commit()
dave = session.query(Musician).first()
print(dave.name)
# 'Dave Grohl'
In this example, we know that if we called dave.name we would receive the string “Dave Grohl”. Here we can see that “name” is an attribute of every instance of the class Musician. Simple enough so far. So let’s complicate things by adding a class for bands.
class Band(Base):
__tablename__ = "band"
id = Column(Integer(), primary_key=True)
name = Column(String())
band1 = Band(name = "Foo Fighters")
session.add(band1)
session.commit()
foo = session.query(Band).first()
class Musician(Base):
__tablename__ = "musician"
id = Column(Integer(), primary_key=True)
name = Column(String())
musician1 = Musician(name = "Dave Grohl")
session.add(musician1)
session.commit()
dave = session.query(Musician).first()
So now we have a table with one band and a table with one band musician, but as far as the code is concerned, the two are not related. So let’s make sure our “musician” table keeps track what band they’re in.
class Musician(Base):
__tablename__ = "musician"
id = Column(Integer(), primary_key=True)
name = Column(String())
band_id = Column(Integer(), ForeignKey('bands.id'))
Great! Now we know that the “musician” table will include a column for the band_id, making it fairly easy to see which band a specific musician belongs to. But much like any relationship in the real world, this is a two way street. And this is where backref makes its debut.
class Band(Base):
__tablename__ = "band"
id = Column(Integer(), primary_key=True)
name = Column(String())
musicians = relationship('Musician', backref=backref('band'))
This will allow us to get quick access to a specific band as if it was an attribute of the Musician class. Since we passed ‘band’ in as the parameter in the backref, we can call the ‘band’ attribute in the exact same way that we called the ‘name’ attribute on the musician previously. Of course, if we did that right now, the return would be the whole instance object, so let’s make it a little easier to read with this __repr__() method:
class Band(Base):
__tablename__ = "band"
id = Column(Integer(), primary_key=True)
name = Column(String())
musicians = relationship('Musician', backref=backref('band'))
def __repr__(self):
return self.name
Ok, now let’s call ‘band’ just like the ‘name’ attribute and we will see what backref does for us:
engine = create_engine('sqlite:///bands.db')
Session = sessionmaker(bind=engine)
session = Session()
class Band(Base):
__tablename__ = "band"
id = Column(Integer(), primary_key=True)
name = Column(String())
musicians = relationship('Musician', backref=backref('band'))
def __repr__(self):
return self.name
band1 = Band(name = "Foo Fighters")
session.add(band1)
session.commit()
class Musician(Base):
__tablename__ = "musician"
id = Column(Integer(), primary_key=True)
name = Column(String())
band_id = Column(Integer(), ForeignKey('bands.id'))
musician1 = Musician(name = "Dave Grohl")
session.add(musician1)
session.commit()
dave = session.query(Musician).first()
print(dave.name)
# 'Dave Grohl'
print(dave.band)
# 'Foo Fighters'
Thanks, backref! This may be the beginning of a beautiful relationship().