20 SQLalchemy concepts with Before-and-After Examples
Table of contents
- 1. Connecting to a Database π
- 2. Defining a Table π
- 3. Creating Tables in the Database ποΈ
- 4. Inserting Data β
- 5. Querying Data π΅οΈββοΈ
- 6. Updating Data βοΈ
- 7. Deleting Data β
- 8. Creating a Relationship (Foreign Keys) π
- 9. Using ORM (Object Relational Mapper) ποΈ
- 10. Querying with ORM π
- 11. Joining Tables π
- 12. Executing Raw SQL Queries π οΈ
- 13. Querying with Filters π
- 14. Aggregations (SUM, AVG, COUNT) π
- 15. Order By (Sorting) ποΈ
- 16. Limiting Results π’
- 17. Transactions (Commit/Rollback) π
- 18. Using Sessions ποΈ
- 19. Eager Loading (Join Load) β‘
- 20. Lazy Loading (Deferred Load) π€
1. Connecting to a Database π
Boilerplate Code:
from sqlalchemy import create_engine
engine = create_engine('sqlite:///mydatabase.db')
Use Case: Connect to a database (e.g., SQLite, PostgreSQL, MySQL) to perform queries and operations.
Goal: Establish a connection to a database using SQLAlchemyβs create_engine
. π―
Sample Code:
from sqlalchemy import create_engine
engine = create_engine('sqlite:///mydatabase.db')
Before Example:
You manually write database connection logic and handle the low-level details of managing connections. π
Manual connection setup, error-prone and inconsistent across databases.
After Example:
With SQLAlchemyβs create_engine
, you can easily connect to any supported database with a single line of code. π
$ python
# Output: Connection to SQLite database established successfully.
Challenge: π Connect to a different database system, like PostgreSQL or MySQL, and query data.
2. Defining a Table π
Boilerplate Code:
from sqlalchemy import Column, Integer, String, MetaData, Table
metadata = MetaData()
users_table = Table(
'users', metadata,
Column('id', Integer, primary_key=True),
Column('name', String),
Column('age', Integer)
)
Use Case: Define the structure of a database table in Python code.
Goal: Use SQLAlchemyβs ORM to define tables using Table
, Column
, and various data types. π―
Sample Code:
from sqlalchemy import Column, Integer, String, MetaData, Table
metadata = MetaData()
users_table = Table(
'users', metadata,
Column('id', Integer, primary_key=True),
Column('name', String),
Column('age', Integer)
)
Before Example:
You manually create tables using raw SQL, making it hard to maintain and scale. π
Creating tables manually using raw SQL queries.
After Example:
With SQLAlchemy, table schemas are defined in Python, and SQLAlchemy translates them into the corresponding SQL queries. π
$ python
# Output: Table "users" defined with columns for ID, name, and age.
Challenge: π Define a more complex table with additional data types like DateTime
, Boolean
, or ForeignKey
.
3. Creating Tables in the Database ποΈ
Boilerplate Code:
metadata.create_all(engine)
Use Case: Automatically create tables in your connected database based on the defined schema.
Goal: Use SQLAlchemyβs create_all()
to generate tables based on your schema. π―
Sample Code:
metadata.create_all(engine)
Before Example:
You manually create tables using SQL commands in your database interface. π
Executing raw SQL to create tables, time-consuming and error-prone.
After Example:
With SQLAlchemy, tables are created automatically in the database based on your schema definition. ποΈ
$ python
# Output: Tables created in the database based on the defined schema.
Challenge: π Try creating multiple tables at once, including those with foreign key relationships.
4. Inserting Data β
Boilerplate Code:
insert_stmt = users_table.insert().values(id=1, name="John", age=30)
conn = engine.connect()
conn.execute(insert_stmt)
Use Case: Insert new records into a database table.
Goal: Insert data using SQLAlchemyβs insert()
method. π―
Sample Code:
insert_stmt = users_table.insert().values(id=1, name="John", age=30)
conn = engine.connect()
conn.execute(insert_stmt)
Before Example:
You manually write SQL insert statements, which can be tedious and repetitive. π
INSERT INTO users (id, name, age) VALUES (1, 'John', 30);
After Example:
With SQLAlchemy, data is inserted in a programmatic way, and itβs easier to handle different databases. β
$ python
# Output: Data inserted into the users table.
Challenge: π Insert multiple rows at once using a loop or a bulk insert method.
5. Querying Data π΅οΈββοΈ
Boilerplate Code:
select_stmt = users_table.select().where(users_table.c.name == 'John')
conn = engine.connect()
result = conn.execute(select_stmt)
for row in result:
print(row)
Use Case: Retrieve data from a database table.
Goal: Use SQLAlchemyβs select()
method to query data with conditions. π―
Sample Code:
select_stmt = users_table.select().where(users_table.c.name == 'John')
conn = engine.connect()
result = conn.execute(select_stmt)
for row in result:
print(row)
Before Example:
You write raw SQL queries to retrieve data, which can be difficult to maintain for complex queries. π
SELECT * FROM users WHERE name = 'John';
After Example:
With SQLAlchemy, queries are more readable and flexible, and they work across different database engines. π΅οΈββοΈ
$ python
# Output: Retrieved data for the user "John".
Challenge: π Write a query that selects multiple users based on more complex conditions (e.g., age greater than 25).
6. Updating Data βοΈ
Boilerplate Code:
update_stmt = users_table.update().where(users_table.c.id == 1).values(name='Jane')
conn = engine.connect()
conn.execute(update_stmt)
Use Case: Update existing data in a table.
Goal: Use SQLAlchemyβs update()
method to modify existing records. π―
Sample Code:
update_stmt = users_table.update().where(users_table.c.id == 1).values(name='Jane')
conn = engine.connect()
conn.execute(update_stmt)
Before Example:
You manually write SQL update statements, which can become cumbersome for multiple fields or complex conditions. π
UPDATE users SET name = 'Jane' WHERE id = 1;
After Example:
With SQLAlchemy, updates are more straightforward and consistent across different database systems. βοΈ
$ python
# Output: User with ID 1 updated to have the name "Jane".
Challenge: π Update multiple rows based on a range of values, and verify the changes.
7. Deleting Data β
Boilerplate Code:
delete_stmt = users_table.delete().where(users_table.c.id == 1)
conn = engine.connect()
conn.execute(delete_stmt)
Use Case: Remove data from a table.
Goal: Use SQLAlchemyβs delete()
method to delete records based on conditions. π―
Sample Code:
delete_stmt = users_table.delete().where(users_table.c.id == 1)
conn = engine.connect()
conn.execute(delete_stmt)
Before Example:
You manually write SQL delete statements, which can be dangerous if not handled carefully. π
DELETE FROM users WHERE id = 1;
After Example:
With SQLAlchemy, deletions are handled in a more controlled and flexible way. β
$ python
# Output: User with ID 1 deleted from the users table.
Challenge: π Write a delete statement that removes records based on a more complex condition, like age or name.
8. Creating a Relationship (Foreign Keys) π
Boilerplate Code:
from sqlalchemy import ForeignKey
posts_table = Table(
'posts', metadata,
Column('id', Integer, primary_key=True),
Column('user_id', Integer, ForeignKey('users.id')),
Column('content', String)
)
Use Case: Establish relationships between tables using foreign keys.
Goal: Use SQLAlchemy to define a relationship between tables via foreign keys. π―
Sample Code:
from sqlalchemy import ForeignKey
posts_table = Table(
'posts', metadata,
Column('id', Integer, primary_key=True),
Column('user_id', Integer, ForeignKey('users.id')),
Column('content', String)
)
Before Example:
You manually write foreign key constraints using raw SQL, which can lead to errors. π
CREATE TABLE posts (id INTEGER, user_id INTEGER, FOREIGN KEY(user_id) REFERENCES users(id));
After Example:
With SQLAlchemy, relationships between tables are handled seamlessly with the ORM. π
$ python
# Output: Foreign key relationship created between users and posts tables.
Challenge: π Create a many-to-many relationship between two
continuing:
tables and implement the logic to query data from both.
9. Using ORM (Object Relational Mapper) ποΈ
Boilerplate Code:
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
Use Case: Map Python classes to database tables.
Goal: Use SQLAlchemyβs ORM to represent tables as Python classes for easier query manipulation. π―
Sample Code:
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
Before Example:
You write raw SQL queries to interact with database tables, making it harder to manage changes. π
Writing manual SQL queries for every interaction with tables.
After Example:
With the ORM, you can query the database using Python objects, making it easier to manage your data model. ποΈ
$ python
# Output: User class defined and mapped to the users table.
Challenge: π Write queries using SQLAlchemyβs ORM to insert, query, update, and delete data.
10. Querying with ORM π
Boilerplate Code:
session.query(User).filter(User.name == 'John').all()
Use Case: Retrieve records using SQLAlchemyβs ORM rather than raw SQL queries.
Goal: Use SQLAlchemyβs ORM to query data using Python objects. π―
Sample Code:
users = session.query(User).filter(User.name == 'John').all()
for user in users:
print(user.name)
Before Example:
You manually write SQL queries to fetch data, which can become repetitive and difficult to maintain. π
SELECT * FROM users WHERE name = 'John';
After Example:
With SQLAlchemyβs ORM, you use simple Python expressions to query the database. π
$ python
# Output: Retrieved users with the name "John".
Challenge: π Write a query using the ORM to retrieve users based on multiple conditions (e.g., name and age).
Here are the SQLAlchemy examples 11-20, following the before-and-after format for data science or machine learning projects:
11. Joining Tables π
Boilerplate Code:
session.query(User, Post).join(Post, User.id == Post.user_id).all()
Use Case: Retrieve data from multiple related tables using joins.
Goal: Use SQLAlchemy's ORM to perform SQL joins and retrieve data from related tables. π―
Sample Code:
session.query(User, Post).join(Post, User.id == Post.user_id).all()
Before Example:
You manually write SQL queries for joins, which can be complex and hard to manage in large projects. π
SELECT * FROM users JOIN posts ON users.id = posts.user_id;
After Example:
With SQLAlchemy, you use Python objects to perform joins easily, making queries more intuitive. π
$ python
# Output: Users and their posts retrieved via join.
Challenge: π Try joining three tables together (e.g., users, posts, comments) and fetching data from all of them.
12. Executing Raw SQL Queries π οΈ
Boilerplate Code:
result = engine.execute("SELECT * FROM users")
for row in result:
print(row)
Use Case: Execute raw SQL queries when needed.
Goal: Use SQLAlchemy to run raw SQL queries when ORM abstractions are not enough. π―
Sample Code:
result = engine.execute("SELECT * FROM users")
for row in result:
print(row)
Before Example:
Sometimes, ORM queries don't cover all use cases, and you need raw SQL for more complex queries. π
Manually executing SQL outside of SQLAlchemyβs ORM framework.
After Example:
With SQLAlchemy, you can still execute raw SQL when needed, while leveraging ORM features for most operations. π οΈ
$ python
# Output: Raw SQL query executed, users retrieved.
Challenge: π Write and execute a raw SQL query for complex joins, aggregations, or window functions.
13. Querying with Filters π
Boilerplate Code:
session.query(User).filter(User.age > 30).all()
Use Case: Filter data based on conditions using SQLAlchemy's ORM.
Goal: Use filter()
to add conditions to your queries. π―
Sample Code:
users_over_30 = session.query(User).filter(User.age > 30).all()
for user in users_over_30:
print(user.name)
Before Example:
You manually construct SQL queries with WHERE clauses, making them prone to errors for large datasets. π
SELECT * FROM users WHERE age > 30;
After Example:
With SQLAlchemyβs ORM, adding filters is straightforward and clean. π
$ python
# Output: All users older than 30 retrieved.
Challenge: π Write a query with multiple filters (e.g., users over 30 and living in a specific city).
14. Aggregations (SUM, AVG, COUNT) π
Boilerplate Code:
from sqlalchemy import func
session.query(func.count(User.id)).scalar()
Use Case: Perform SQL aggregations like COUNT
, SUM
, AVG
.
Goal: Use SQLAlchemy to aggregate data with built-in SQL functions. π―
Sample Code:
from sqlalchemy import func
user_count = session.query(func.count(User.id)).scalar()
print(f"Total users: {user_count}")
Before Example:
Manually executing aggregation queries, which can lead to verbose code. π
SELECT COUNT(*) FROM users;
After Example:
With SQLAlchemyβs func
, you can easily perform aggregations in Python. π
$ python
# Output: Total users retrieved.
Challenge: π Perform other aggregations like SUM
of user ages or AVG
post likes.
15. Order By (Sorting) ποΈ
Boilerplate Code:
session.query(User).order_by(User.name).all()
Use Case: Sort query results by one or more columns.
Goal: Use SQLAlchemyβs order_by()
to sort results. π―
Sample Code:
sorted_users = session.query(User).order_by(User.name).all()
for user in sorted_users:
print(user.name)
Before Example:
Sorting data with raw SQL can make queries verbose. π
SELECT * FROM users ORDER BY name;
After Example:
With SQLAlchemyβs ORM, you can easily sort results in a readable manner. ποΈ
$ python
# Output: Users sorted by name.
Challenge: π Sort by multiple columns, for example by age and name.
16. Limiting Results π’
Boilerplate Code:
session.query(User).limit(5).all()
Use Case: Retrieve a specific number of records.
Goal: Use limit() to restrict the number of results returned by a query. π―
Sample Code:
limited_users = session.query(User).limit(5).all()
for user in limited_users:
print(user.name)
Before Example:
Manually handling pagination and limiting results in raw SQL. π
SELECT * FROM users LIMIT 5;
After Example:
With SQLAlchemy, limiting results is easy and works across different database systems. π’
$ python
# Output: First 5 users retrieved.
Challenge: π Add offset()
to your query to skip the first few results (for pagination).
17. Transactions (Commit/Rollback) π
Boilerplate Code:
session.add(new_user)
session.commit()
Use Case: Commit changes to the database or roll them back in case of failure.
Goal: Use commit() and rollback() to manage transactions. π―
Sample Code:
session.add(new_user)
try:
session.commit()
except:
session.rollback()
raise
Before Example:
You manually manage transactions and error handling, which can be tricky and error-prone. π
Manually writing commit and rollback logic with SQL.
After Example:
With SQLAlchemy, transactions are handled cleanly with commit and rollback methods. π
$ python
# Output: Data committed to the database, or rolled back on failure.
Challenge: π Write a test case that intentionally triggers an error and verify that the transaction is rolled back.
18. Using Sessions ποΈ
Boilerplate Code:
from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind=engine)
session = Session()
Use Case: Manage database operations in an isolated session.
Goal: Use SQLAlchemy sessions to interact with the database within an isolated environment. π―
Sample Code:
Session = sessionmaker(bind=engine)
session = Session()
Before Example:
Manually managing multiple database connections and commits, which can lead to race conditions. π
Manually handling multiple connections in code.
After Example:
With SQLAlchemy sessions, database operations are isolated and safely handled. ποΈ
$ python
# Output: Session created, ready for database operations.
Challenge: π Use multiple sessions to simulate concurrent database operations.
19. Eager Loading (Join Load) β‘
Boilerplate Code:
from sqlalchemy.orm import joinedload
session.query(User).options(joinedload(User.posts)).all()
Use Case: Retrieve related data in a single query to avoid multiple database calls.
Goal: Use eager loading to improve query performance by joining related tables. π―
Sample Code:
from sqlalchemy.orm import joinedload
users_with_posts = session.query(User).options(joinedload(User.posts)).all()
for user in users_with_posts:
print(user.posts)
Before Example:
You make separate database queries for each related record, resulting in slow performance. π
Multiple queries to retrieve users and their posts.
After Example:
With eager loading, related data is fetched in one query, improving performance. β‘
$ python
# Output: Users and their posts retrieved in a single query.
Challenge: π Implement eager loading for deeply nested relationships (e.g., users β posts β comments).
20. Lazy Loading (Deferred Load) π€
Boilerplate Code:
session.query(User).all() # No posts loaded yet
for user in users:
print(user.posts) #
It looks like we left off with **lazy loading** in SQLAlchemy. Let's finish **concept 20** and wrap up the **SQLAlchemy** examples 11-20!
---
### 20. **Lazy Loading (Deferred Load)** π€
**Boilerplate Code**:
```python
session.query(User).all() # No posts loaded yet
for user in users:
print(user.posts) # Posts are loaded when accessed
Use Case: Load related data only when it's accessed.
Goal: Use lazy loading to defer loading related data until it is needed, optimizing memory and performance. π―
Sample Code:
users = session.query(User).all() # Posts are not loaded yet
for user in users:
print(user.posts) # Posts loaded only when accessed
Before Example:
You load all related records immediately, even if they are not needed, wasting memory and time. π
User data and their posts are all loaded at once, even if posts aren't used in the code.
After Example:
With lazy loading, related data is loaded only when accessed, reducing memory usage and improving efficiency. π€
$ python
# Output: Users loaded, and posts are only fetched when accessed.
Challenge: π Implement lazy loading for other related data, like comments or likes, and measure the performance difference.
Subscribe to my newsletter
Read articles from Anix Lynch directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by