0Pricing
FastAPI Backend Development Bootcamp · レッスン

SQLAlchemyによるCRUD操作

SQLAlchemy ORMを使って、APIエンドポイントの作成、読み取り、更新、削除(CRUD)操作を実装します。

「SQLAlchemyによるCRUD操作」はCoddyKit上の無料FastAPI Backend Development Bootcampレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応の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時間対応のAIチューター)、FastAPI Backend Development Bootcampコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 FastAPI Backend Development Bootcampコースには全4レッスンが含まれています。

「SQLAlchemyによるCRUD操作」で何を学びますか?

SQLAlchemy ORMを使って、APIエンドポイントの作成、読み取り、更新、削除(CRUD)操作を実装します。 ブラウザで直接実行するハンズオンコードでFastAPI Backend Development Bootcampを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

FastAPI Backend Development Bootcampを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのFastAPI Backend Development Bootcampは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「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に戻る