0Pricing
FastAPI Backend Development Bootcamp · 강의

SQLAlchemy를 활용한 CRUD 작업

SQLAlchemy ORM을 사용하여 API 엔드포인트에 생성, 조회, 수정, 삭제 작업을 구현합니다.

SQLAlchemy를 활용한 CRUD 작업은(는) CoddyKit의 무료 FastAPI Backend Development Bootcamp 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 FastAPI Backend Development Bootcamp 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. FastAPI Backend Development Bootcamp 강의에는 총 4개의 강의가 포함되어 있습니다.

이 강의의 일부는 아직 번역되지 않았으며 영어로 표시됩니다.

CRUD Operations: The Core

Welcome to Lesson 3! Today, we'll master CRUD operations using FastAPI and SQLAlchemy. CRUD stands for:

  • Create: Adding new data.
  • Read: Retrieving existing data.
  • Update: Modifying existing data.
  • Delete: Removing data.

These four operations are the foundation of almost any application that interacts with a database.

Setup: Models & Session

Before diving into CRUD, let's set up our SQLAlchemy model and Pydantic schemas. We'll use an in-memory SQLite database for our runnable examples.

First, our SQLAlchemy Todo model to represent a task:

from sqlalchemy import Column, Integer, String, Boolean
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Todo(Base):
    __tablename__ = "todos"
    id = Column(Integer, primary_key=True, index=True)
    title = Column(String, index=True)
    description = Column(String, default="")
    completed = Column(Boolean, default=False)

And Pydantic schemas for request/response:

from pydantic import BaseModel

class TodoCreate(BaseModel):
    title: str
    description: str = ""
    completed: bool = False

class TodoResponse(TodoCreate):
    id: int

    class Config:
        orm_mode = True

Database Session in FastAPI

In FastAPI, we manage database sessions using dependencies. This ensures each request gets a fresh session and it's properly closed.

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db"
engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

This get_db function will be injected into our FastAPI endpoints.

Create: FastAPI Endpoint

To Create a new item, we'll use a POST request. The endpoint will receive data via a Pydantic model and use SQLAlchemy to add it to the database.

from fastapi import FastAPI, Depends
from sqlalchemy.orm import Session

# ... (imports for Base, Todo, TodoCreate, TodoResponse, get_db, engine)

app = FastAPI()
Base.metadata.create_all(bind=engine) # Create tables

@app.post("/todos/", response_model=TodoResponse)
def create_todo(todo: TodoCreate, db: Session = Depends(get_db)):
    db_todo = Todo(title=todo.title, description=todo.description, completed=todo.completed)
    db.add(db_todo)
    db.commit()
    db.refresh(db_todo) # Refresh to get ID and updated fields
    return db_todo

The db.refresh() call updates our db_todo object with any database-generated values, like the id.

Create: SQLAlchemy Demo

Let's see the SQLAlchemy 'Create' steps in action. This runnable script will add a new todo to our in-memory database.

from sqlalchemy import create_engine, Column, Integer, String, Boolean
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

# 1. Setup Database and Model
SQLALCHEMY_DATABASE_URL = "sqlite:///:memory:"
engine = create_engine(SQLALCHEMY_DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()

class Todo(Base):
    __tablename__ = "todos"
    id = Column(Integer, primary_key=True, index=True)
    title = Column(String, index=True)
    description = Column(String, default="")
    completed = Column(Boolean, default=False)

# 2. Create Tables
Base.metadata.create_all(bind=engine)

# 3. Create a Session
db = SessionLocal()

# 4. Create Operation (C)
print("--- Creating a new Todo ---")
new_todo = Todo(title="Learn FastAPI", description="Complete CoddyKit lesson", completed=False)
db.add(new_todo)
db.commit()
db.refresh(new_todo)
print(f"Created Todo: ID={new_todo.id}, Title='{new_todo.title}'")

# 5. Close Session
db.close()

Read: FastAPI Endpoints

To Read data, we use GET requests. We'll have two endpoints: one to get all todos, and another to get a single todo by its ID.

# ... (FastAPI app, imports, get_db, models)

@app.get("/todos/", response_model=list[TodoResponse])
def read_todos(db: Session = Depends(get_db)):
    todos = db.query(Todo).all()
    return todos

@app.get("/todos/{todo_id}", response_model=TodoResponse)
def read_todo(todo_id: int, db: Session = Depends(get_db)):
    todo = db.query(Todo).filter(Todo.id == todo_id).first()
    if todo is None:
        raise HTTPException(status_code=404, detail="Todo not found")
    return todo

We use .all() to get a list and .first() to get a single item. Remember to handle cases where an item isn't found!

Read: SQLAlchemy Demo

Let's run a demo for the 'Read' operation. We'll first create a few todos, then fetch them all, and finally fetch a specific one by ID.

from sqlalchemy import create_engine, Column, Integer, String, Boolean
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

# Setup Database and Model (same as before)
SQLALCHEMY_DATABASE_URL = "sqlite:///:memory:"
engine = create_engine(SQLALCHEMY_DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()

class Todo(Base):
    __tablename__ = "todos"
    id = Column(Integer, primary_key=True, index=True)
    title = Column(String, index=True)
    description = Column(String, default="")
    completed = Column(Boolean, default=False)

Base.metadata.create_all(bind=engine)
db = SessionLocal()

# Create some initial Todos for reading
todo1 = Todo(title="Buy groceries")
todo2 = Todo(title="Walk the dog")
db.add_all([todo1, todo2])
db.commit()
db.refresh(todo1)
db.refresh(todo2)
print(f"Created Todos: {todo1.id}, {todo2.id}\n")

# Read Operation (R)
print("--- Reading all Todos ---")
all_todos = db.query(Todo).all()
for t in all_todos:
    print(f"ID={t.id}, Title='{t.title}'")

print("\n--- Reading a specific Todo (ID 1) ---")
specific_todo = db.query(Todo).filter(Todo.id == 1).first()
if specific_todo:
    print(f"Found Todo: ID={specific_todo.id}, Title='{specific_todo.title}'")
else:
    print("Todo with ID 1 not found.")

print("\n--- Reading a non-existent Todo (ID 99) ---")
non_existent_todo = db.query(Todo).filter(Todo.id == 99).first()
if non_existent_todo:
    print(f"Found Todo: ID={non_existent_todo.id}")
else:
    print("Todo with ID 99 not found.")

db.close()

Update: FastAPI Endpoint

The Update operation (PUT or PATCH) allows us to modify an existing item. We'll typically find the item by ID, update its attributes, and commit the changes.

# ... (FastAPI app, imports, get_db, models)

class TodoUpdate(BaseModel):
    title: str | None = None
    description: str | None = None
    completed: bool | None = None

@app.put("/todos/{todo_id}", response_model=TodoResponse)
def update_todo(todo_id: int, todo_update: TodoUpdate, db: Session = Depends(get_db)):
    db_todo = db.query(Todo).filter(Todo.id == todo_id).first()
    if db_todo is None:
        raise HTTPException(status_code=404, detail="Todo not found")
    
    # Update fields only if provided
    for key, value in todo_update.dict(exclude_unset=True).items():
        setattr(db_todo, key, value)
    
    db.add(db_todo) # Re-add to session for update tracking
    db.commit()
    db.refresh(db_todo)
    return db_todo

Using exclude_unset=True in Pydantic ensures only provided fields are updated.

Update: SQLAlchemy Demo

Here's a runnable example showing how to update a todo item's title and completion status using SQLAlchemy.

from sqlalchemy import create_engine, Column, Integer, String, Boolean
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

# Setup Database and Model
SQLALCHEMY_DATABASE_URL = "sqlite:///:memory:"
engine = create_engine(SQLALCHEMY_DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()

class Todo(Base):
    __tablename__ = "todos"
    id = Column(Integer, primary_key=True, index=True)
    title = Column(String, index=True)
    description = Column(String, default="")
    completed = Column(Boolean, default=False)

Base.metadata.create_all(bind=engine)
db = SessionLocal()

# Create an initial Todo to update
initial_todo = Todo(title="Draft report", completed=False)
db.add(initial_todo)
db.commit()
db.refresh(initial_todo)
print(f"Created Todo: ID={initial_todo.id}, Title='{initial_todo.title}', Completed={initial_todo.completed}\n")

# Update Operation (U)
print(f"--- Updating Todo ID {initial_todo.id} ---")
todo_to_update = db.query(Todo).filter(Todo.id == initial_todo.id).first()

if todo_to_update:
    todo_to_update.title = "Finalize report"
    todo_to_update.completed = True
    db.commit()
    db.refresh(todo_to_update)
    print(f"Updated Todo: ID={todo_to_update.id}, Title='{todo_to_update.title}', Completed={todo_to_update.completed}")
else:
    print(f"Todo with ID {initial_todo.id} not found.")

db.close()

Delete: FastAPI Endpoint

The Delete operation removes an item from the database. This is usually done with a DELETE request, targeting an item by its ID.

# ... (FastAPI app, imports, get_db, models)

@app.delete("/todos/{todo_id}", status_code=204) # 204 No Content for successful deletion
def delete_todo(todo_id: int, db: Session = Depends(get_db)):
    db_todo = db.query(Todo).filter(Todo.id == todo_id).first()
    if db_todo is None:
        raise HTTPException(status_code=404, detail="Todo not found")
    
    db.delete(db_todo)
    db.commit()
    return {"message": "Todo deleted successfully"} # FastAPI automatically handles 204

A successful deletion often returns a 204 No Content status code, meaning the request was fulfilled but there's no content to send back.

Delete: SQLAlchemy Demo

Let's run a script to demonstrate deleting a todo item. We'll create one, then delete it, and try to read it again to confirm its removal.

from sqlalchemy import create_engine, Column, Integer, String, Boolean
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

# Setup Database and Model
SQLALCHEMY_DATABASE_URL = "sqlite:///:memory:"
engine = create_engine(SQLALCHEMY_DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()

class Todo(Base):
    __tablename__ = "todos"
    id = Column(Integer, primary_key=True, index=True)
    title = Column(String, index=True)
    description = Column(String, default="")
    completed = Column(Boolean, default=False)

Base.metadata.create_all(bind=engine)
db = SessionLocal()

# Create an initial Todo to delete
todo_to_delete = Todo(title="Clean garage")
db.add(todo_to_delete)
db.commit()
db.refresh(todo_to_delete)
print(f"Created Todo: ID={todo_to_delete.id}, Title='{todo_to_delete.title}'\n")

# Delete Operation (D)
print(f"--- Deleting Todo ID {todo_to_delete.id} ---")
db.delete(todo_to_delete)
db.commit()
print(f"Todo ID {todo_to_delete.id} deleted.\n")

# Verify deletion by trying to read it
print("--- Verifying deletion ---")
verify_deleted = db.query(Todo).filter(Todo.id == todo_to_delete.id).first()
if verify_deleted is None:
    print(f"Successfully verified: Todo ID {todo_to_delete.id} is no longer in the database.")
else:
    print(f"Error: Todo ID {todo_to_delete.id} still found.")

db.close()

CRUD Challenge

You've learned the core CRUD operations! Now, let's test your understanding of how SQLAlchemy methods map to these operations.

Recap & Next Steps

Great job! In this lesson, you've learned to implement the fundamental CRUD operations in FastAPI using SQLAlchemy:

  • Create (POST): Using db.add(), db.commit(), and db.refresh().
  • Read (GET): Using db.query().all() for lists and db.query().filter().first() for single items.
  • Update (PUT): Fetching an item, modifying its attributes, then db.commit() and db.refresh().
  • Delete (DELETE): Fetching an item, then db.delete() and db.commit().

These skills are crucial for building any data-driven API. In the next course, we'll dive into advanced topics like user authentication and authorization to secure your API endpoints!

자주 묻는 질문

“SQLAlchemy를 활용한 CRUD 작업” 강의는 무료인가요?

네 — “SQLAlchemy를 활용한 CRUD 작업” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 FastAPI Backend Development Bootcamp 강의 전체를 잠금 해제할 수 있습니다. FastAPI Backend Development Bootcamp 강의에는 총 4개의 강의가 포함되어 있습니다.

“SQLAlchemy를 활용한 CRUD 작업”에서 뭘 배우나요?

SQLAlchemy ORM을 사용하여 API 엔드포인트에 생성, 조회, 수정, 삭제 작업을 구현합니다. 브라우저에서 직접 실행하는 실습 코드로 FastAPI Backend Development Bootcamp을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

FastAPI Backend Development Bootcamp을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 FastAPI Backend Development Bootcamp은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.

“SQLAlchemy를 활용한 CRUD 작업” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 FastAPI Backend Development Bootcamp 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 FastAPI Backend Development Bootcamp 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. SQLAlchemy ORM 기초
  2. FastAPI와 PostgreSQL 연결
  3. SQLAlchemy를 활용한 CRUD 작업
  4. Alembic을 활용한 데이터베이스 마이그레이션
← FastAPI Backend Development Bootcamp(으)로 돌아가기