Delete a Model Object using SQLModel

2 min read

This is going to be the final part of CRUD.

Alright, time to complete our CRUD quadlogy! with the most destructive operation - DELETE! This is where we permanently remove blog posts from existence. No undo button, no recycle bin, just skadoosh - gone forever! 💥

Which anime MC is getting skadooshed : r/PowerScaling

The CRUD Layer Finale

Let's add our delete function to database/crud/blog.py:

def delete_blog_by_slug(slug: str, db: Session) -> Optional[bool]:
    statement = select(Blog).where(Blog.slug == slug)
    result = db.exec(statement)
    db_blog = result.first()
    if not db_blog:
        return None
    db.delete(db_blog)
    db.commit()
    return True

The Familiar Pattern: We start with the same old pattern- find the blog by slug using select() and where(). By now, this should feel like muscle memory!

The Point of No Return: db.delete(db_blog) marks the record for deletion. But here's the thing - it's not actually gone yet! SQLAlchemy or SQLModel just marks it for deletion in the current transaction.

The Final Commit: db.commit() is where the magic (or tragedy) happens. This is when the blog actually gets kicked out from the database.

The Route Layer

Now for the DELETE endpoint in apis/v1/blog.py:

@router.delete("/blogs/{slug}", status_code=status.HTTP_200_OK)
def delete_blog(slug: str, db: Session = Depends(get_db)):
    if delete_blog_by_slug(slug, db):
        return {"detail": "Blog deleted successfully"}
    raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Blog not found")

Status Code Choice: We're using 200 OK instead of 204 No Content. Both are valid for DELETE operations, but 204 is often preferred since there's literally no content to return. But returning a success message is nice for frontend developers, so I went with 200.

No Response Model: Unlike our other endpoints, this DELETE route doesn't have a response_model. That's actually fine since we're just returning a simple dict, but it's inconsistent with our other endpoints.

Alternative Approaches

Return the Deleted Object: Some APIs return the deleted object before destroying it. Useful for "undo" functionality:

def delete_blog_by_slug(slug: str, db: Session) -> Blog:
    statement = select(Blog).where(Blog.slug == slug)
    result = db.exec(statement)
    db_blog = result.first()
    if not db_blog:
        return None
    
    # Store the blog data before deleting
    deleted_blog = db_blog
    db.delete(db_blog)
    db.commit()
    return deleted_blog

Soft Delete: In many real-world apps, you don't actually delete data - you just mark it as "deleted" with a flag or timestamp. Users can't see it, but you can restore it if needed:

def soft_delete_blog_by_slug(slug: str, db: Session = Depends(get_db)) -> Blog:
    # ... find blog logic ...
    db_blog.is_deleted = True
    db_blog.deleted_at = datetime.utcnow()
    db.commit()
    return db_blog

 

Testing it out:

Time to test our delete functionality:

 

What We've Built

Congratulations! We now have a complete CRUD API:

  • ✅ Create blogs with POST
  • ✅ Read blogs with GET (single and list)
  • ✅ Update blogs with PUT
  • ✅ Delete blogs with DELETE
FastAPITutorial

My priority is to help build a production-ready application using FastAPI

I prioritize quality over simplicity. Our challenging approach ensures you're prepared for real-world development.

Contacts

Refunds:

Refund Policy
Social

Follow us on our social media channels to stay updated with our latest tutorials and resources.

© Copyright 2022-2025 Team FastAPITutorial. All rights reserved.