Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions crud.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from sqlalchemy.orm import Session
from schemas import AuthorCreate, BookCreate
import models


def get_author(db: Session, author_id: int):
return db.query(models.Author).filter(models.Author.id == author_id).first()


def get_authors(db: Session, skip: int, limit: int):
return db.query(models.Author).offset(skip).limit(limit).all()


def create_author(db: Session, author: AuthorCreate):
db_author = models.Author(**author.model_dump())
db.add(db_author)
db.commit()
db.refresh(db_author)
return db_author


def get_books(db: Session, skip: int, limit: int, author_id: int = None):
query = db.query(models.Book)
if author_id:
query = query.filter(models.Book.author_id == author_id)
return query.offset(skip).limit(limit).all()


def create_book(db: Session, book: BookCreate):
db_book = models.Book(**book.model_dump())
db.add(db_book)
db.commit()
db.refresh(db_book)
return db_book
12 changes: 12 additions & 0 deletions database.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from sqlalchemy import create_engine
from sqlalchemy.orm import declarative_base
from sqlalchemy.orm import sessionmaker

SQLALCHEMY_DATABASE_URL = "sqlite:///./library.db"

engine = create_engine(
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

Base = declarative_base()
48 changes: 48 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from sqlalchemy.orm import Session
import crud
from database import engine, SessionLocal
from models import Base
from schemas import AuthorCreate, AuthorResponse, BookResponse, BookCreate
from fastapi import FastAPI, Depends, HTTPException
from typing import List


Base.metadata.create_all(bind=engine)

app = FastAPI()

def get_db():
db = SessionLocal()
try:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The BookCreate schema requires author_id, but in the POST /authors/{author_id}/books endpoint, the author_id is taken from the URL path. Users would need to provide author_id in both the path AND the request body, which is redundant. Consider creating a separate schema for book creation that excludes author_id, or accepting it only from the request body.

yield db
finally:
db.close()

@app.post("/authors", response_model=AuthorResponse)
def create_author(author: AuthorCreate, db: Session = Depends(get_db)):
return crud.create_author(db=db, author=author)


@app.get("/authors", response_model=List[AuthorResponse])
def read_authors(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
return crud.get_authors(db, skip=skip, limit=limit)


@app.get("/authors/{author_id}", response_model=AuthorResponse)
def read_author(author_id: int, db: Session = Depends(get_db)):
author = crud.get_author(db, author_id=author_id)
if not author:
raise HTTPException(status_code=404, detail="Author not found")
return author

@app.post("/authors/{author_id}/books", response_model=BookResponse)
def create_book(author_id: int, book: BookCreate, db: Session = Depends(get_db)):
author = crud.get_author(db, author_id=author_id)
if not author:
raise HTTPException(status_code=404, detail="Author not found")
return crud.create_book(db=db, book=book)


@app.get("/books", response_model=List[BookResponse])
def read_books(skip: int = 0, limit: int = 100, author_id: int = None, db: Session = Depends(get_db)):
return crud.get_books(db, skip=skip, limit=limit, author_id=author_id)
24 changes: 24 additions & 0 deletions models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from datetime import date
from sqlalchemy import String, ForeignKey
from sqlalchemy.orm import Mapped, mapped_column, relationship
from database import Base


class Author(Base):
__tablename__ = "author"

id: Mapped[int] = mapped_column(primary_key=True, index=True)
name: Mapped[str] = mapped_column(String(255), nullable=False, unique=True)
bio: Mapped[str] = mapped_column(String(511), nullable=False)
books: Mapped[list["Book"]] = relationship(back_populates="author")


class Book(Base):
__tablename__ = "book"

id: Mapped[int] = mapped_column(primary_key=True, index=True)
title: Mapped[str] = mapped_column(String(255), nullable=False,)
summary: Mapped[str]
publication_date: Mapped[date]
author_id: Mapped[int] = mapped_column(ForeignKey("author.id"))
author : Mapped["Author"] = relationship(back_populates="books")
33 changes: 33 additions & 0 deletions schemas.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from pydantic import BaseModel, ConfigDict
from datetime import date


class AuthorBase(BaseModel):
name: str
bio: str


class AuthorCreate(AuthorBase):
pass


class AuthorResponse(AuthorBase):
id: int
books: list["BookResponse"]
model_config = ConfigDict(from_attributes=True)


class BookBase(BaseModel):
title: str
summary: str
publication_date: date
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The author_id field in BookCreate creates a conflict with the endpoint design. When calling POST /authors/{author_id}/books, the author_id comes from the URL path, not the request body. Including it in BookCreate is redundant and could lead to data inconsistency if different values are passed in URL vs body. The author relationship should be established using the URL path parameter instead.

author_id: int


class BookCreate(BookBase):
pass


class BookResponse(BookBase):
id: int
model_config = ConfigDict(from_attributes=True)