SQLAlchemy in Python: Tutorial, ORM & CRUD Operations

SQLAlchemy is a Python SQL toolkit and ORM that simplifies working with relational databases such as SQLite, PostgreSQL, and MySQL. Learn how to create database models, manage sessions, define relationships, and perform CRUD operations using practical Python examples. Explore SQLAlchemy Core vs ORM, best practices, and integration with Flask and FastAPI for building efficient, maintainable backend applications.

SQLAlchemy is one of the most widely used Python libraries for interacting with relational databases. It helps developers manage database connections, execute SQL queries, define relationships between tables, and perform CRUD operations using Python.

Instead of manually writing SQL queries for every database operation, developers can use SQLAlchemy’s Object Relational Mapper (ORM) to work with database records as Python objects. It also provides SQLAlchemy Core for developers who need more direct control over SQL expressions and database operations.

In this guide, you will learn how SQLAlchemy works, how to connect Python to a database, define ORM models, manage sessions, perform CRUD operations, and integrate SQLAlchemy with Flask and FastAPI.

SQLAlchemy in Python_ Tutorial, ORM & CRUD Operations

What Is SQLAlchemy in Python?

SQLAlchemy is an open-source SQL toolkit and Object Relational Mapper (ORM) for Python. It connects Python applications with relational databases such as SQLite, PostgreSQL, MySQL, and Oracle.

An ORM maps database tables to Python classes and database rows to Python objects. This allows developers to create, retrieve, update, and delete database records through Python code.

For example, a database table named users can be represented by a Python class called User. Each instance of that class can represent a user record.

SQLAlchemy supports two main approaches:

  • SQLAlchemy Core: Builds SQL expressions and provides direct access to tables, connections, and transactions.
  • SQLAlchemy ORM: Maps Python classes to database tables and manages records through sessions.

These approaches can be used independently or together, depending on the application’s requirements.

SQLAlchemy Core vs ORM

Understanding the difference between SQLAlchemy Core and ORM helps developers choose the right approach for a project.

FeatureSQLAlchemy CoreSQLAlchemy ORM
Main purposeSQL expression and database accessObject-relational mapping
Data representationTables and rowsPython classes and objects
Query approachSQL expressionsORM queries and SQL expressions
Relationship handlingExplicit joins and table operationsObject relationships
Best suited forFine-grained SQL controlApplication data models
Transaction supportYesYes, through the session and underlying connection

For example, SQLAlchemy Core can select data directly from a table, while the ORM can retrieve records as Python objects.

Both approaches use SQLAlchemy’s underlying database connectivity and SQL expression capabilities.

registor_now_P

Installing SQLAlchemy and Creating a Database Engine

Install SQLAlchemy using pip:

pip install sqlalchemy

For this example, SQLite is used because it does not require a separate database server.

Create a database engine:

from sqlalchemy import create_engine

engine = create_engine(
    "sqlite:///blog.db",
    echo=True
)

The create_engine() function configures the database connection interface and manages connections through a connection pool where appropriate.

The sqlite:///blog.db URL specifies a SQLite database file named blog.db in the current working directory.

The echo=True parameter prints SQL statements and related logging information, making it useful for learning and debugging.

Defining SQLAlchemy Models in Python

A SQLAlchemy ORM model is a Python class mapped to a database table. In SQLAlchemy 2.0, the declarative mapping style uses DeclarativeBase, Mapped, and mapped_column().

The following example defines a User model and a Post model with a one-to-many relationship.

from sqlalchemy import String, ForeignKey
from sqlalchemy.orm import (
    DeclarativeBase,
    Mapped,
    mapped_column,
    relationship
)

class Base(DeclarativeBase):
    pass


class User(Base):
    __tablename__ = "users"

    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(String(50))
    email: Mapped[str] = mapped_column(
        String(100),
        unique=True
    )

    posts: Mapped[list["Post"]] = relationship(
        back_populates="author"
    )


class Post(Base):
    __tablename__ = "posts"

    id: Mapped[int] = mapped_column(primary_key=True)
    title: Mapped[str] = mapped_column(String(120))

    user_id: Mapped[int] = mapped_column(
        ForeignKey("users.id")
    )

    author: Mapped["User"] = relationship(
        back_populates="posts"
    )


Base.metadata.create_all(engine)

Here is what the main components do:

  • __tablename__ specifies the database table name.
  • mapped_column() defines mapped columns and their properties.
  • primary_key=True identifies the primary key column.
  • ForeignKey("users.id") links a post to a user.
  • relationship() defines the object-level relationship between the models.
  • Base.metadata.create_all(engine) creates the mapped tables that do not already exist.

A user can have multiple posts, while each post references one user. The back_populates argument connects both sides of the relationship.

Note: create_all() creates missing tables but does not automatically migrate existing tables when the model structure changes. For ongoing schema changes, use a migration tool such as Alembic.

SQLAlchemy Session Management

A SQLAlchemy session manages ORM objects and coordinates database operations within a unit of work. It tracks changes to objects and flushes those changes to the database when required.

The following example creates a user and a related post, commits the transaction, and retrieves the user.

from sqlalchemy import select
from sqlalchemy.orm import Session

with Session(engine) as session:
    user = User(
        name="Naveen",
        email="naveen@example.com"
    )

    user.posts.append(
        Post(title="Learning SQLAlchemy")
    )

    session.add(user)
    session.commit()

    stmt = select(User).where(
        User.name == "Naveen"
    )

    found = session.scalars(stmt).first()

    if found:
        print(found.email)
        print(len(found.posts))

The Session context manager closes the session when the block ends. The commit() method commits the transaction, while select() constructs a query using SQLAlchemy’s expression API.

The session also tracks related objects, so the user and post can be persisted together through the same transaction.

In production applications, use short-lived sessions and handle database errors appropriately. If a flush or commit fails, call session.rollback() before reusing that session.

SQLAlchemy CRUD Operations

CRUD stands for Create, Read, Update, and Delete. These four operations form the foundation of most database-driven applications.

1. Create Records

Add a new object to the session and commit it.

with Session(engine) as session:
    user = User(
        name="Priya",
        email="priya@example.com"
    )

    session.add(user)
    session.commit()

2. Read Records

Use select() to retrieve data from the database.

with Session(engine) as session:
    stmt = select(User).where(
        User.name == "Priya"
    )

    user = session.scalars(stmt).first()

    if user:
        print(user.email)

3. Update Records

Load the record, modify its attributes, and commit the changes.

with Session(engine) as session:
    user = session.scalars(
        select(User).where(User.name == "Priya")
    ).first()

    if user:
        user.name = "Priya Sharma"
        session.commit()

SQLAlchemy detects the modified attribute and generates the appropriate UPDATE statement when changes are flushed.

4. Delete Records

Use session.delete() to mark a persistent object for deletion.

with Session(engine) as session:
    user = session.scalars(
        select(User).where(
            User.name == "Priya Sharma"
        )
    ).first()

    if user:
        session.delete(user)
        session.commit()

This example deletes the user record. If related posts exist, the database’s foreign-key rules and the ORM relationship configuration determine how deletion is handled. Configure deletion behavior deliberately rather than assuming related records will always be removed automatically.

CRUD Operations at a Glance

OperationSQLAlchemy ORM approachPurpose
Createsession.add(obj) and session.commit()Insert a record
Readsession.scalars(select(Model)).all()Retrieve records
UpdateModify object attributes and commitChange existing data
Deletesession.delete(obj) and commitRemove a record

Explore Courses - Learn More

SQLAlchemy Relationships and Foreign Keys

Database relationships connect related information stored in separate tables. SQLAlchemy ORM supports common relationship patterns, including one-to-many, many-to-one, one-to-one, and many-to-many.

In the example above, a user can publish multiple posts. The user_id foreign key stores the relationship in the database, while User.posts and Post.author provide convenient object-level navigation.

Relationships are useful in blogging platforms, e-commerce applications, learning management systems, and customer management systems.

For example, an e-commerce database might connect customers to orders and orders to order items. Properly defined foreign keys and ORM relationships help maintain data integrity and make related records easier to query.

SQLAlchemy with Flask and FastAPI

SQLAlchemy is commonly used in Python backend development with frameworks such as Flask and FastAPI.

  • With Flask: The Flask-SQLAlchemy extension integrates SQLAlchemy with Flask’s application context and provides convenient database access patterns.
  • With FastAPI: A common approach is to provide a session through a dependency function and ensure that it is cleaned up after the request.

Both frameworks benefit from separating database models, request validation, business logic, and API routes. Pydantic is commonly used with FastAPI to validate request data and serialize response data.

For larger applications, this separation makes testing, debugging, and maintaining database operations easier.

SQLAlchemy Best Practices

Following good database practices helps make SQLAlchemy applications more reliable, secure, and maintainable.

  • Use short-lived sessions: Create an appropriate session scope for each request or unit of work instead of sharing one global session across requests.
  • Handle transaction failures: Roll back a session after a failed flush or commit before attempting further operations.
  • Use migrations: Manage schema changes with Alembic rather than relying on create_all() to modify existing tables.
  • Avoid the N+1 query problem: Use loading strategies such as selectinload() when retrieving collections of related objects.
  • Use parameterized expressions: Prefer SQLAlchemy expressions over manually concatenating untrusted input into SQL strings.
  • Configure relationships carefully: Choose appropriate foreign-key constraints, loading strategies, and deletion behavior.
  • Manage transactions deliberately: Commit successful changes and roll back failed transactions to preserve data consistency.

These practices are particularly important in production applications where multiple users, concurrent requests, and complex database relationships are involved.

Common SQLAlchemy Mistakes Beginners Should Avoid

One common mistake is forgetting to commit a transaction, which can leave intended changes unsaved. Another is sharing a single session between concurrent requests or tasks, which can cause incorrect session usage and difficult-to-debug behavior.

Beginners may also assume that Base.metadata.create_all() updates existing database tables. It does not provide a complete migration mechanism, so schema changes should be handled with migrations.

Finally, avoid constructing SQL statements by joining user input into strings. SQLAlchemy’s expression system supports parameter binding, helping protect queries from SQL injection when used correctly.

Understanding these details early helps developers build safer and more predictable Python database applications.

Conclusion

SQLAlchemy in Python provides a flexible way to interact with relational databases through both SQL expressions and object-relational mapping. By learning engines, models, sessions, relationships, and CRUD operations, beginners can develop a strong foundation for backend development.

Start with SQLite, inspect generated SQL using echo=True, and practise creating, reading, updating, and deleting records. As your projects grow, explore Alembic migrations, transaction management, query optimization, and integration with Flask or FastAPI.

With these concepts in place, SQLAlchemy becomes a practical tool for building maintainable, database-driven Python applications.

 

Talk to Academic Advisor

Frequently Asked Questions

SQLAlchemy connects Python applications to relational databases. It supports SQL query construction, database connections, transactions, ORM models, and CRUD operations.

It is both. SQLAlchemy Core provides SQL expression and database connectivity features, while SQLAlchemy ORM maps Python classes to database tables.

It depends on the use case. SQLAlchemy reduces repetitive database code and provides ORM features, while raw SQL can be useful for specialized queries. Developers can use both approaches in the same application.

Yes. SQLAlchemy supports multiple database backends, although database-specific features, connection drivers, data types, and SQL behavior can differ.

A session manages ORM objects, tracks their changes, and coordinates database transactions. Developers use it to add, retrieve, update, and delete records.

SQLAlchemy handles database connectivity, SQL expressions, and ORM operations. Alembic is a database migration tool commonly used with SQLAlchemy to manage schema changes over time.

Author

Embedded Systems and IOT Trainer– IIES

Updated On: 28-10-26


10+ years of hands-on experience delivering practical training in Embedded Systems and it's design