DEV Community

Jimmy Li
Jimmy Li

Posted on

What's the difference between back_ref and back_populates, and why does it matter?

Introduction
Hello Readers, my name is Jimmy Li and I welcome you to my blog. This will be my third blog as I progress through my software development journey at Flatiron School. Today, I will be talking about SQLAlchemy Relationships, namely, how to use back_ref and back_populates to create relationships between two or more tables without having to write a ton of SQL statements.

What is SQLAlchemy?
SQLAlchemy is a Object Relational Mapper or ORM that allows a programmer to interact with databases using Python Objects instead of writing SQL queries. What does this mean? This means that we can build tables like we would using SQL as well as create relationships between those tables using primary and foreign keys. In SQLAlchemy, we can specify a Foreign key column as well as use the built in relationship() method to provide an way for one model to access properties or attributes of its related model and vice versa. I will demonstrate this idea in an example below:

Example

from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    username = Column(String)
    posts = relationship("Post", backref="user")

class Post(Base):
    __tablename__ = 'posts'
    id = Column(Integer, primary_key=True)
    title = Column(String)
    user_id = Column(Integer, ForeignKey('users.id'))

Enter fullscreen mode Exit fullscreen mode

In this example, we have defined a one-to-many relationship between two SQLAlchemy models, 'User' and 'Post'. It is one-to-many because a user can have many posts, and a post can belong to only one user. This can be seen in the schema for Post, where there is a user_id column that defines a ForeignKey that references a user. This reference is what allows a Post to be linked back to it's respective user through it's ID. Another thing of note is the use of the relationship constructor in the User model.

What is relationship?
Relationship is a constructor that is part of a module imported from SQLAlchemy ORM that provides a property to be defined in the "many" part of the relationship (in this case the User) to access all of the related models in the "one". In the example above, the posts attribute is how a User is able to access all of their related Posts.

For example: If we were to create a User instance:

#assuming all the proper imports

user = User("James")

Enter fullscreen mode Exit fullscreen mode

We can access all of the posts made by James using the .posts attribute that was created by relationship.


user.posts

->[] #return a list containing Post objects
Enter fullscreen mode Exit fullscreen mode

Inside of the relationship constructor, you may have noticed 2 parameters, a string and the key-word back_ref. The back_ref parameter is what actually establishes the bi-directional relationship between User and Posts. It is one of the two parameters, the other one being back_populates, which allow you to create a reference between two related models. For back_ref however, it only creates a simple reference, so keep that in mind.

Back_populates
Now that I have explained back_ref, I will now delve into back_populates. back_populates lets you establish a more fine-grained control over how the bi-directional relationship is managed. Instead of only establishing the relationship on one side of the model, back_populates requires you to specify both sides of the relationship explicitly, and this is particularly useful when you need to ensure data consistency and integrity. Let's look at the same example above but using back_populates.

Example

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    username = Column(String)
    posts = relationship("Post", back_populates="user")

class Post(Base):
    __tablename__ = 'posts'
    id = Column(Integer, primary_key=True)
    title = Column(String)
    author_id = Column(Integer, ForeignKey('users.id'))
    user = relationship("User", back_populates="posts")
Enter fullscreen mode Exit fullscreen mode

In this version of the model, both table schemas have a relationship that uses back_populates that is set to the attributes "author" and "posts" respectively. In this example, a User is able to access their posts through the "posts" attribute, and a Post is able to access the user using the "user" attribute.

Conclusion

In summary, the main distinction that I want to get across in the difference between 'back_ref' and 'back_populates' in SQLAlchemy is the level of control that they provide. 'back_ref' simplifies the setup of the relationship by establishing it automatically, while 'back_populates' gives you complete control on both ends. The choice between which one to use will depend on how complex your application's data models are and the level of control you want.

By understanding these concepts and employing them appropriately, you can harness the full power of SQLAlchemy to create well-structured and efficient database-driven applications.

Top comments (0)