🗄️ Flask Database Operations: Your Data Kitchen Adventure
Analogy: Think of your database like a giant recipe box in a kitchen. CRUD operations are how you add new recipe cards, read them, update them when you discover better ingredients, and throw away the ones you don’t need anymore.
🍳 What Are Database Operations?
Imagine you run a magical kitchen. Your database is your recipe box, and Flask-SQLAlchemy is your helpful kitchen assistant who organizes everything for you.
In real life:
- When you save a contact on your phone → that’s Create
- When you look up someone’s number → that’s Read
- When you change their address → that’s Update
- When you delete an old contact → that’s Delete
📝 CRUD Operations: The Four Magic Spells
Create - Adding New Recipe Cards
When you want to save something new to your database, you create it.
# Create a new user
new_user = User(
name="Alice",
email="alice@example.com"
)
db.session.add(new_user)
db.session.commit()
What’s happening?
- You write a new recipe card (
User(...)) - You hand it to your assistant (
db.session.add()) - Your assistant files it permanently (
db.session.commit())
Read - Finding Recipe Cards
Reading is how you look up information that’s already saved.
# Find one user by ID
user = User.query.get(1)
# Find all users
all_users = User.query.all()
# Find first matching user
first_user = User.query.first()
Think of it like:
get(1)→ “Give me recipe card #1”all()→ “Show me ALL recipe cards”first()→ “Just show me the first one you find”
Update - Fixing Recipe Cards
Sometimes you need to change information that’s already saved.
# Find the user first
user = User.query.get(1)
# Change their email
user.email = "newemail@example.com"
# Save the change
db.session.commit()
Simple as:
- Find the card
- Erase and rewrite
- Put it back in the box
Delete - Throwing Away Recipe Cards
When something is no longer needed, you delete it.
# Find the user
user = User.query.get(1)
# Remove them
db.session.delete(user)
db.session.commit()
⚠️ Warning: Once deleted and committed, it’s gone forever!
🔍 Database Query Methods: Different Ways to Search
graph TD A["Start Query"] --> B{What do you need?} B -->|One specific item| C[".get id"] B -->|All items| D[".all"] B -->|First match| E[".first"] B -->|With conditions| F[".filter_by"] B -->|Complex conditions| G[".filter"]
The Search Methods
| Method | What It Does | Returns |
|---|---|---|
.get(id) |
Find by ID | One item or None |
.all() |
Get everything | List of items |
.first() |
Get first match | One item or None |
.one() |
Expect exactly one | One item (or error!) |
.count() |
Count matches | A number |
# Examples
user = User.query.get(5)
users = User.query.all()
admin = User.query.first()
total = User.query.count()
⛓️ Query Chaining: Building Complex Searches
Query chaining is like adding more instructions to your search. Each method adds another filter, like a series of sieves.
# Find active admins, sorted by name
results = User.query \
.filter_by(role='admin') \
.filter_by(is_active=True) \
.order_by(User.name) \
.all()
Think of it like:
- “Start with all users” →
User.query - “Keep only admins” →
.filter_by(role='admin') - “Keep only active ones” →
.filter_by(is_active=True) - “Sort by name” →
.order_by(User.name) - “Give me the list” →
.all()
Common Chain Methods
.filter() # Add conditions
.filter_by() # Simple equality filters
.order_by() # Sort results
.limit() # Max number of results
.offset() # Skip first N results
.distinct() # Remove duplicates
🎯 Advanced Query Filters: Precision Searching
Sometimes filter_by() isn’t enough. That’s when you use filter() with special operators.
Comparison Operators
# Greater than
User.query.filter(User.age > 18).all()
# Less than or equal
User.query.filter(User.age <= 65).all()
# Not equal
User.query.filter(User.name != 'Admin').all()
# Between values
User.query.filter(
User.age.between(18, 30)
).all()
Text Search Magic
# Contains text (like %word%)
User.query.filter(
User.name.like('%john%')
).all()
# Case-insensitive search
User.query.filter(
User.name.ilike('%JOHN%')
).all()
# Starts with
User.query.filter(
User.email.startswith('admin')
).all()
Combining Conditions
from sqlalchemy import and_, or_, not_
# AND: Both must be true
User.query.filter(
and_(User.age > 18, User.is_active == True)
).all()
# OR: Either can be true
User.query.filter(
or_(User.role == 'admin', User.role == 'mod')
).all()
# NOT: Opposite of condition
User.query.filter(
not_(User.is_banned)
).all()
The IN Operator
# Find users with specific IDs
User.query.filter(
User.id.in_([1, 2, 3, 4, 5])
).all()
# Find users NOT in list
User.query.filter(
~User.role.in_(['banned', 'suspended'])
).all()
đź“„ Pagination: Serving Data in Portions
Imagine you have 10,000 recipes. You can’t show them all at once! Pagination breaks data into pages.
graph LR A["All 100 Users"] --> B["Page 1: Users 1-10"] A --> C["Page 2: Users 11-20"] A --> D["Page 3: Users 21-30"] A --> E["... more pages"]
Basic Pagination
# Get page 1, with 10 items per page
page = User.query.paginate(
page=1,
per_page=10
)
# Access the items
users = page.items
# Navigation info
page.has_next # Is there a next page?
page.has_prev # Is there a previous page?
page.next_num # Next page number
page.prev_num # Previous page number
page.pages # Total number of pages
page.total # Total number of items
In Your Flask Route
@app.route('/users')
def list_users():
page_num = request.args.get(
'page', 1, type=int
)
users = User.query.paginate(
page=page_num,
per_page=20,
error_out=False
)
return render_template(
'users.html',
pagination=users
)
🎠Database Session Management: The Behind-the-Scenes Crew
The session is like a notepad where you write all your changes before making them permanent.
graph TD A["Your Changes"] --> B["Session Notepad"] B -->|commit| C["Database - Permanent!"] B -->|rollback| D["Thrown Away"]
Key Session Operations
# Add new item to notepad
db.session.add(new_user)
# Add multiple items
db.session.add_all([user1, user2, user3])
# Make changes permanent
db.session.commit()
# Undo all changes in notepad
db.session.rollback()
# Refresh item from database
db.session.refresh(user)
# Remove item from session
db.session.expunge(user)
Why Use Sessions?
Without sessions: Every change goes directly to database = SLOW and RISKY
With sessions:
- Batch multiple changes together
- Review before saving
- Undo mistakes easily
đź”’ Database Transactions: All or Nothing
A transaction groups operations together. Either ALL succeed, or NONE of them happen.
The Bank Transfer Example
Imagine transferring $100 from Alice to Bob:
try:
# Start transaction (automatic)
# Step 1: Take from Alice
alice.balance -= 100
# Step 2: Give to Bob
bob.balance += 100
# All good? Save everything!
db.session.commit()
except Exception as e:
# Something went wrong!
# Undo EVERYTHING
db.session.rollback()
raise e
Why transactions matter:
- If Bob’s account update fails, Alice gets her money back
- No money disappears into thin air
- Data stays consistent
Using Context Manager
from flask_sqlalchemy import SQLAlchemy
# Clean way to handle transactions
def transfer_money(from_id, to_id, amount):
try:
sender = User.query.get(from_id)
receiver = User.query.get(to_id)
sender.balance -= amount
receiver.balance += amount
db.session.commit()
return True
except:
db.session.rollback()
return False
Nested Transactions with Savepoints
# Create a savepoint
db.session.begin_nested()
try:
# Risky operation
user.credits -= 50
db.session.commit() # Commits savepoint
except:
db.session.rollback() # Only rolls back to savepoint
🎯 Quick Reference: Your Cheat Codes
CRUD in 4 Lines Each
# CREATE
db.session.add(User(name="X"))
db.session.commit()
# READ
user = User.query.get(1)
# UPDATE
user.name = "New Name"
db.session.commit()
# DELETE
db.session.delete(user)
db.session.commit()
Filter Shortcuts
| Want This? | Use This |
|---|---|
| Equals | filter_by(name='X') |
| Greater than | filter(User.age > 18) |
| Contains | filter(User.name.like('%X%')) |
| In list | filter(User.id.in_([1,2,3])) |
| Multiple AND | Chain .filter() calls |
| OR condition | filter(or_(a, b)) |
🌟 You Did It!
You now understand:
âś… CRUD - Create, Read, Update, Delete âś… Query Methods - Different ways to fetch data âś… Query Chaining - Building complex searches âś… Advanced Filters - Precision searching âś… Pagination - Handling large datasets âś… Sessions - Managing changes âś… Transactions - Keeping data safe
Remember: Your database is your recipe box. Flask-SQLAlchemy is your kitchen assistant. Together, you can cook up amazing applications! 🍳✨
