CRUD Operations

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.

Learn CRUD Operations in our free Flask course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick reference.

Part of the free Flask course at LearnCodingFast — hands-on lessons with examples you run in your browser, 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.

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:

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.

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 .

Because the object is tracked by the session, simply assigning product.price = 39.99 stages an update; commit() writes it to the database.

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 :

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.

Complete the code below. Replace each ___ to create a note, update it, then delete it.

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.

Lesson 12 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.

🚀 Up next: Database Migrations — evolve your schema safely as your models change.

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().