Sling Academy
Home/SQLAlchemy/SQLAlchemy Upsert: Update if Exists, Insert if Not

SQLAlchemy Upsert: Update if Exists, Insert if Not

Last updated: January 03, 2024

Introduction

When working with databases, a common task is to either insert a new record or update an existing one. This tutorial explores how to execute an ‘upsert’ operation in SQLAlchemy, ensuring an efficient way to handle record creation or modification.

Understanding Upsert in SQLAlchemy

An upsert is a database operation that inserts rows into a database table if they do not already exist, or updates them if they do. SQLAlchemy, a popular SQL toolkit and Object-Relational Mapping (ORM) library for Python, provides several ways to accomplish this task.
First, let’s get our environment set up.

# Install SQLAlchemy
pip install sqlalchemy

Import the necessary modules from SQLAlchemy:

from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

Basics of Upserting

Let’s start by creating a simple model that represents our database table:

Base = declarative_base()

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String)
    email = Column(String)

We then establish a connection to our database:

engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine))
Session = sessionmaker(bind=engine)
session = Session()

Naive Upsert Approach

One might attempt a naive upsert by checking if a record exists first and then deciding to either update or insert.

instance = session.query(User).filter_by(email='[email protected]').first()
if instance:
    # Update the record
    instance.name = 'New Name'
    session.commit()
else:
    # Insert the record
    new_user = User(name='New Name', email='[email protected]')
    session.add(new_user)
    session.commit()

Using SQL Expressions for Upserts

SQLAlchemy core provides a more efficient upsert mechanism using SQL expression language:

from sqlalchemy.dialects.postgresql import insert

insert_stmt = insert(User).values(name='New Name', email='[email protected]')
upsert_stmt = insert_stmt.on_conflict_do_update(
    index_elements=['email'],
    set_={'name': insert_stmt.excluded.name}
)
session.execute(upsert_stmt)
session.commit()

This approach effectively combines the insert and update operations into a single atomic action that is more efficient and reliable.

Advanced Usage of Upserts with Custom Conflict Actions

SQLAlchemy allows for even more control over upserts with custom conflict actions:

# Additional imports for advanced upsert
from sqlalchemy.dialects.postgresql import excludable as exc

upsert_stmt = insert(User).
    values(name='John Doe', email='[email protected]').
    on_conflict_do_update(
        constraint='users_email_key',
        set_={'name': exc.name}
    )
session.execute(upsert_stmt)
session.commit()

In this example, we use a predefined constraint (‘users_email_key’) to handle conflicts more precisely, allowing for a tailored upsert operation.

Conclusion

SQLAlchemy provides a versatile toolkit for managing database operations, including upserts. With upserts, you can maintain database integrity and improve performance by avoiding separate insert and update transactions. Whether you are working on a small project or a large-scale application, mastering upserts in SQLAlchemy can lead to more efficient and robust database handling.

Next Article: How to Safely Alter a Table in SQLAlchemy

Previous Article: SQLAlchemy: How to Delete a Record by ID

Series: SQLAlchemy Tutorials: From Basic to Advanced

SQLAlchemy

You May Also Like

  • SQLAlchemy: Counting rows for each category (2 approaches)
  • SQLAlchemy: Adding a calculated column to SELECT query
  • SQLAlchemy: Grouping data on multiple columns
  • SQLAlchemy: How to temporarily delete a row
  • SQLAlchemy Composite Indexes: The Complete Guide
  • Full-Text Search in SQLAlchemy: The Ultimate Guide
  • SQLAlchemy: What if you don’t close database connections?
  • SQLAlchemy: How to Remove FOREIGN KEY Constraints (2 Ways)
  • SQLAlchemy: How to Create and Use Temporary Tables
  • SQLAlchemy: Saving Categories and Subcategories in the Same Table
  • SQLAlchemy: How to Automatically Delete Old Records
  • Weighted Random Selection in SQLAlchemy: An In-Depth Guide
  • SQLAlchemy: created_at and updated_at columns
  • How to Use Regular Expressions in SQLAlchemy
  • SQLAlchemy: Ways to Find Results by a Keyword
  • SQLAlchemy: How to Connect to MySQL Database
  • SQLAlchemy: How to Bulk Insert Data into a Table
  • SQLAlchemy: How to Update a Record by ID
  • SQLAlchemy: Singular vs Plural Table Names