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

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.99

Because 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

OperationSQLAlchemy code
Createdb.session.add(obj) then commit
ReadModel.query.all() / .get(id)
Updateobj.attr = value then commit
Deletedb.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 list

Common 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.

TaskCommand / Code
Createdb.session.add(obj)
Read oneModel.query.get_or_404(id)
Updateobj.attr = value; commit()
Deletedb.session.delete(obj)

🏆 Mini Challenge: Manage Users

Run a full CRUD cycle on a user record.

# 🏆 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

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.