CRUD Operations
Reviewed & published by Brayan K
CRUD stands for Create, Read, Update, and Delete — the four fundamental operations every data-driven application performs on its records, and with Flask-SQLAlchemy each one is just a few lines of Python.
Part of the free Flask course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.
In this lesson you'll insert new rows, query existing ones, modify records in place, and remove them, building the complete lifecycle of database-backed data.
What You'll Learn in This Lesson
- What the four CRUD operations are
- Creating rows with add and commit
- Reading rows with query, get, and filter_by
- Updating a record by changing attributes
- Deleting a record with db.session.delete
- Using get_or_404 inside routes
1 Create and Read
Create means inserting a new row: build a model instance, db.session.add() it, then db.session.commit(). Read means querying: query.all() for every row, query.get(id) for one by primary key.
The code below creates a few products, then reads them back:
# app.py — Create and Read
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///shop.db"
db = SQLAlchemy(app)
class Product(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False)
price = db.Column(db.Float, nullable=False)
with app.app_context():
db.create_all()
# CREATE: stage and save two products.
db.session.add(Product(name="Keyboard", price=49.99))
db.session.add(Product(name="Mouse", price=24.50))
db.session.commit()
# READ: every row, then one row by id.
for p in Product.query.all():
print(p.id, p.name, p.price)
one = Product.query.get(1)
print("Product 1 is:", one.name)query.get(1) looks up the row whose primary key is 1. In a route you'd often use get_or_404(1) so a missing record returns a clean 404 page. Run flask run to serve routes built on this.
2 Update a Record
Update is wonderfully simple in SQLAlchemy: fetch the object, change its attributes in plain Python, and commit(). The ORM detects the change and writes it — you never type an SQL UPDATE.
# app.py — Update an existing row
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///shop.db"
db = SQLAlchemy(app)
class Product(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False)
price = db.Column(db.Float, nullable=False)
with app.app_context():
db.create_all()
db.session.add(Product(name="Keyboard", price=49.99))
db.session.commit()
# UPDATE: fetch, modify attributes, commit.
product = Product.query.filter_by(name="Keyboard").first()
product.price = 39.99
db.session.commit()
print("New price:", Product.query.get(product.id).price)
# ✅ Expected output:
# New price: 39.99Because the object is tracked by the session, simply assigning product.price = 39.99 stages an update; commit() writes it to the database.
3 Delete a Record
Delete removes a row: fetch the object, pass it to db.session.delete(), then commit(). Here it is wired into a real route using get_or_404:
# app.py — Delete inside a route
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///shop.db"
db = SQLAlchemy(app)
class Product(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False)
# A route that deletes a product by id.
@app.route("/products/<int:product_id>/delete", methods=["POST"])
def delete_product(product_id):
# get_or_404 returns the row or a clean 404 page.
product = Product.query.get_or_404(product_id)
db.session.delete(product)
db.session.commit()
return f"Deleted product {product_id}"
with app.app_context():
db.create_all()
db.session.add(Product(name="Mouse"))
db.session.commit()
# Simulate the delete logic directly.
p = Product.query.get(1)
db.session.delete(p)
db.session.commit()
print("Remaining products:", Product.query.all())
# ✅ Expected output:
# Remaining products: []Delete routes use POST rather than GET so a page cannot accidentally remove data just by being loaded. Try flask run and submit a form that posts to this route.
🔬 The Four CRUD Operations
| Operation | SQLAlchemy code |
|---|---|
| Create | db.session.add(obj) then commit |
| Read | Model.query.all() / .get(id) |
| Update | obj.attr = value then commit |
| Delete | db.session.delete(obj) then commit |
🎯 Your Turn: Fill in the Blanks
Complete the code below. Replace each ___ to create a note, update it, then delete it.
# 🎯 YOUR TURN: replace each ___ to perform full CRUD
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///notes.db"
db = SQLAlchemy(app)
class Note(db.Model):
id = db.Column(db.Integer, primary_key=True)
text = db.Column(db.String(200), nullable=False)
with app.app_context():
db.create_all()
note = Note(text="First draft")
db.session.___(note) # CREATE: stage the new row
db.session.commit()
note.text = "Final version" # UPDATE: change an attribute
db.session.commit()
db.session.___(note) # DELETE: remove the row
db.session.commit()
print(Note.query.___()) # READ: should be an empty listCommon Errors & Quick Reference
❌ My update or delete did not stick
Every change needs db.session.commit(). Changing an attribute or calling delete() only stages the change until you commit.
❌ AttributeError: 'NoneType' object has no attribute ...
query.get(id) returns None when no row matches. Use get_or_404(id) in routes, or check for None before using the result.
| Task | Command / Code |
|---|---|
| Create | db.session.add(obj) |
| Read one | Model.query.get_or_404(id) |
| Update | obj.attr = value; commit() |
| Delete | db.session.delete(obj) |
🏆 Mini Challenge: Manage Users
Run a full CRUD cycle on a user record.
- Create a user named "sam"
- Read it back and print the row
- Update the username to "samantha", then delete the user
# 🏆 Mini Challenge: full CRUD on a user
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///users.db"
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), nullable=False)
with app.app_context():
db.create_all()
user = User(username="sam") # CREATE
db.session.add(user)
db.session.commit()
print("Created:", User.query.get(user.id).username) # READ
user.username = "samantha" # UPDATE
db.session.commit()
print("Updated:", User.query.get(user.id).username)
db.session.delete(user) # DELETE
db.session.commit()
print("Remaining:", User.query.all())❓ Frequently Asked Questions
Lesson complete — you can manage data end to end!
You now create, read, update, and delete records with Flask-SQLAlchemy, and you know how get_or_404 protects routes against missing rows.
Practice quiz
What does CRUD stand for?
- Copy, Read, Use, Delete
- Create, Read, Update, Delete
- Cache, Run, Update, Drop
- Connect, Resolve, Undo, Deploy
Answer: Create, Read, Update, Delete. CRUD is Create, Read, Update, and Delete — the four basic data operations.
Which two calls insert a new row in SQLAlchemy?
- db.session.add(obj) then db.session.commit()
- db.insert(obj)
- obj.save()
- db.session.create(obj)
Answer: db.session.add(obj) then db.session.commit(). Stage with db.session.add(obj), then persist with db.session.commit().
How do you fetch one row by its primary key?
- Model.query.first(id)
- Model.find(id)
- Model.query.get(id)
- Model.query.all(id)
Answer: Model.query.get(id). Model.query.get(id) returns the row with that primary key or None.
How do you update a record with SQLAlchemy?
- call db.update(obj)
- run a raw UPDATE statement
- obj.refresh()
- change the attribute then db.session.commit()
Answer: change the attribute then db.session.commit(). Edit the object's attributes in Python, then commit; the ORM writes the change.
Which call removes a row?
- db.session.delete(obj) then commit
- obj.remove()
- Model.query.drop(obj)
- db.session.pop(obj)
Answer: db.session.delete(obj) then commit. Pass the object to db.session.delete(obj), then commit to remove the row.
What does Model.query.get_or_404(id) do when no row matches?
- returns None
- raises a 404 Not Found
- returns an empty list
- creates the row
Answer: raises a 404 Not Found. get_or_404 returns the row, or aborts with a clean 404 if it does not exist.
Why must you call db.session.commit() after changes?
- to print the row
- to open the database
- without it changes are only staged, not saved
- to create the table
Answer: without it changes are only staged, not saved. add, delete, and attribute edits are staged until commit() writes them.
Why do delete routes typically use POST instead of GET?
- GET is deprecated
- POST is faster
- POST encrypts data
- so a loaded page cannot accidentally delete data
Answer: so a loaded page cannot accidentally delete data. GET requests can fire from links/prefetch; POST avoids accidental deletes.
What does Product.query.all() return?
- a list of all rows
- the first row only
- a count
- the table name
Answer: a list of all rows. query.all() returns every row of the model as a list.
Which method filters rows by a column value?
- query.where(name='x')
- query.filter_by(name='x')
- query.match('x')
- query.select('x')
Answer: query.filter_by(name='x'). filter_by(name='x') returns a query filtered on that column; add .first() or .all().
Continue this course
- Previous: Database with SQLAlchemy
- Next: Database Migrations — Evolve your database schema safely with Flask-Migrate and Alembic
- Quick reference: Flask cheat sheet
Frequently asked questions
What does CRUD stand for?
CRUD stands for Create, Read, Update, and Delete — the four basic operations every data-driven app performs on its records. With Flask-SQLAlchemy each maps to a few lines of Python.
How do I update a record with SQLAlchemy?
Query the object you want, change its attributes in Python like obj.title = 'New', then call db.session.commit(). SQLAlchemy tracks the change and writes it on commit — no UPDATE statement needed.
How do I delete a record?
Fetch the object, pass it to db.session.delete(obj), then commit. The row is removed from the table once the session commits.
What is get_or_404 used for?
Model.query.get_or_404(id) returns the row with that primary key, or automatically raises a 404 Not Found if no such row exists — handy in routes that look up records by id.