How to Save a Python List in SQLAlchemy

Updated: January 3, 2024 By: Guest Contributor Post a comment

Overview

SQLAlchemy is a popular Object Relational Mapper (ORM) for Python that allows developers to interact with databases in a more Pythonic manner. Instead of writing raw SQL queries, you can use Python classes and objects to represent and manipulate your database tables and records. This guide will demonstrate the process of saving a Python list to a database using SQLAlchemy, a typical requirement in many web and data-driven applications.

Setting up the Environment

Before we dive into the code examples, let’s set up our environment. First, ensure that SQLAlchemy is installed in your Python environment:

pip install SQLAlchemy

If you need to install a database driver, such as psycopg2 for PostgreSQL or pymysql for MySQL, do so accordingly:

pip install psycopg2
pip install pymysql

Next, create a database and configure your SQLAlchemy engine, session, and base class:

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

DATABASE_URI = 'dialect+driver://username:password@host:port/database'

engine = create_engine(DATABASE_URI)
Session = sessionmaker(bind=engine)
session = Session()
Base = declarative_base()

Model Definition

Let’s define a simple model, a User with a list of favorite numbers:

from sqlalchemy import Column, Integer, String
from sqlalchemy.dialects.postgresql import ARRAY

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

Base.metadata.create_all(engine)

The above model uses PostgreSQL’s ARRAY to store a list directly. If you’re not using PostgreSQL or prefer a more database-agnostic approach, you can use a secondary table and foreign keys.

Basic List Storage

To save a list, simply assign it to the User instance and add it to the session:

user = User(name='Alice', favorite_numbers=[7, 42, 99])
session.add(user)
session.commit()

This approach is straightforward when the list is part of a single record.

Advanced List Storage and Retrieval

For a more generalized solution, consider a many-to-one relationship between the User and a new Number entity:

from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship

class Number(Base):
    __tablename__ = 'numbers'
    id = Column(Integer, primary_key=True)
    value = Column(Integer)
    user_id = Column(Integer, ForeignKey('users.id'))

    user = relationship('User', back_populates='favorite_numbers')

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String)
    favorite_numbers = relationship('Number', order_by=Number.id, back_populates='user')

Base.metadata.create_all(engine)

In the new model, every number is an independent record linked to a user. It’s also possible to create a many-to-many relationship if the same number can belong to multiple users. For this, you would need an associative table.

Data Serialization and Conversion

What if you need to keep the list within a single column but your database doesn’t support arrays? You can use serialization:

from sqlalchemy.types import TypeDecorator, String
import json

class JSONEncodedDict(TypeDecorator):
    impl = String

    def process_bind_param(self, value, dialect):
        if value is not None:
            value = json.dumps(value)
        return value
    def process_result_value(self, value, dialect):
        if value is not None:
            value = json.loads(value)
        return value

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

Base.metadata.create_all(engine)

This uses a custom column type to automatically serialize and deserialize the list using JSON when storing and retrieving it from the database.

Working with JSONB

In databases like PostgreSQL, you can take advantage of JSONB for more flexibility and performance:

from sqlalchemy.dialects.postgresql import JSONB

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

Base.metadata.create_all(engine)

This allows you to perform queries directly on the JSON structures within the database.

Conclusion

In conclusion, storing a Python list in SQLAlchemy can be achieved through various methods, including using array types, separate tables for relationships, or serialization techniques. Whichever method you choose, SQLAlchemy provides the flexibility to work with lists and arrays efficiently, as showcased in these examples. Always consider the specific requirements and constraints of your application and database environment when making your decision.