Updating a Model Object with SQLModel

3 min read

Time to tackle one of the trickier parts of any CRUD API - updating existing records! Today we're building the update functionality for our blog posts. This is where things get a bit more interesting because we need to find the blog first, then modify it.

The CRUD Layer Update

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

from typing import Optional


def update_blog_by_slug(slug: str, blog: CreateBlog, db: Session) -> Optional[Blog]:
    statement = select(Blog).where(Blog.slug == slug)
    result = db.exec(statement)
    db_blog = result.first()
    if not db_blog:
        return None
    db_blog.title = blog.title
    db_blog.slug = blog.slug
    db_blog.content = blog.content
    db.commit()
    db.refresh(db_blog)
    return db_blog

Breaking Down the Update Logic

Find First: We're using the same pattern as our get function - find the blog by slug using SQLModel's select() and where().

The None Check: If the blog doesn't exist, we return None instead of throwing an exception right here in the CRUD layer. This keeps our separation of concerns clean - the CRUD layer handles data operations, the route layer handles HTTP responses.

Field by Field Updates: Here's where it gets interesting. We're manually updating each field:

  • db_blog.title = blog.title
  • db_blog.slug = blog.slug
  • db_blog.content = blog.content

The Commit Dance: After updating the fields, we call db.commit() to save changes to the database, then db.refresh(db_blog) to get any updated fields back e.g. the timestamp fields of created_at, updated_at.

The Route Layer

Now let's add the PUT endpoint to apis/v1/blog.py:

@router.put("/blogs/{slug}", response_model=ShowBlog)
def update_blog(slug: str, blog: CreateBlog, db: Session = Depends(get_db)):
    blog = update_blog_by_slug(slug, blog, db)
    if not blog:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Blog not found")
    return blog

PUT vs PATCH: We're using PUT here, which in REST conventions means "replace the entire resource." If we wanted partial updates, we'd use PATCH. But for simplicity, full replacement works fine for most scenarios.

Error Handling: If the CRUD function returns None, we throw a 404. Otherwise, FastAPI automatically serializes our updated blog using the ShowBlog schema.

Slug Updates Are Dangerous: Notice we allow updating the slug? That means the URL someone used to access the blog could change after they update it. That's... weird. In real apps, you might want to keep slugs immutable or handle redirects.

No Validation on Duplicate Slugs: What happens if someone updates their blog to use a slug that already exists? Database constraint violation! We should probably check for that.

The Manual Field Assignment Problem

That manual field-by-field update is going to get annoying fast. What if you add 10 more fields to your blog model? You'll have to remember to update this function every time.

Here's a cleaner approach using SQLModel's syntax:

def update_blog_by_slug(slug: str, blog: CreateBlog, db: Session) -> Optional[Blog]:
    statement = select(Blog).where(Blog.slug == slug)
    result = db.exec(statement)
    db_blog = result.first()
    if not db_blog:
        return None
    
    # Update only the fields that were provided
    blog_data = blog.dict(exclude_unset=True)
    for field, value in blog_data.items():
        setattr(db_blog, field, value)
    
    db.commit()
    db.refresh(db_blog)
    return db_blog

But that's getting fancy - let's stick with explicit updates for now. Clear is better than clever!

Testing Your Update

You should get back the updated blog with all your changes applied!

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.