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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ coverage.xml
local_settings.py
db.sqlite3
db.sqlite3-journal
library.db

# Flask stuff:
instance/
Expand Down Expand Up @@ -128,3 +129,5 @@ dmypy.json
# Pyre type checker
.pyre/
.idea/

.vscode/
82 changes: 82 additions & 0 deletions crud.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
from sqlalchemy.orm import Session
import models
from fastapi import HTTPException
import schemas


def get_book_by_id(book_id: int, db: Session):
return db.query(models.Book).filter(models.Book.id == book_id).first()


def get_book_by_title(title: str, db: Session):
return db.query(models.Book).filter(models.Book.title == title).first()


def create_book(author_id: int, book: schemas.BookCreate, db: Session):
if not get_author_by_id(author_id, db):
raise HTTPException(status_code=404, detail="Author not found")
db_book = models.Book(**book.model_dump(), author_id=author_id)
db.add(db_book)
db.commit()
db.refresh(db_book)
return db_book


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


def get_author_by_name(name: str, db: Session):
return db.query(models.Author).filter(models.Author.name == name).first()


def create_author(author: schemas.AuthorCreate, db: Session):
if get_author_by_name(author.name, db):
raise HTTPException(
status_code=400, detail="Author with this name already exists"
)
db_author = models.Author(**author.model_dump())
db.add(db_author)
db.commit()
db.refresh(db_author)
return db_author


def get_all_books(db: Session, skip: int = 0, limit: int = 100):
return db.query(models.Book).offset(skip).limit(limit).all()


def get_books_by_author(author_id: int, db: Session, skip: int = 0, limit: int = 100):
return (
db.query(models.Book)
.filter(models.Book.author_id == author_id)
.offset(skip)
.limit(limit)
.all()
)


def get_all_authors(db: Session, skip: int = 0, limit: int = 100):
return db.query(models.Author).offset(skip).limit(limit).all()


def get_authors_with_books(db: Session):
return db.query(models.Author).filter(models.Author.books.any()).all()


def delete_book(book_id: int, db: Session):
book = get_book_by_id(book_id, db)
if not book:
raise HTTPException(status_code=404, detail="Book not found")
db.delete(book)
db.commit()
return {"detail": "Book deleted"}


def delete_author(author_id: int, db: Session):
author = get_author_by_id(author_id, db)
if not author:
raise HTTPException(status_code=404, detail="Author not found")
db.delete(author)
db.commit()
return {"detail": "Author deleted"}
14 changes: 14 additions & 0 deletions database.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative 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()
74 changes: 74 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
from sqlalchemy.orm import Session
from fastapi import FastAPI, Depends, HTTPException

from database import SessionLocal, engine
import models
import schemas
import crud

models.Base.metadata.create_all(bind=engine)

app = FastAPI()


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


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


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


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


@app.delete("/authors/{author_id}")
def delete_author(author_id: int, db: Session = Depends(get_db)):
return crud.delete_author(author_id, db)


@app.post("/authors/{author_id}/books", response_model=schemas.BookResponse)
def create_book(
book: schemas.BookCreate, author_id: int, db: Session = Depends(get_db)
):
return crud.create_book(author_id, book, db)


@app.get("/books", response_model=list[schemas.BookResponse])
def read_books(
db: Session = Depends(get_db),
author_id: int | None = None,
skip: int = 0,
limit: int = 100,
):
if author_id is not None:
return crud.get_books_by_author(author_id, db, skip=skip, limit=limit)
return crud.get_all_books(db, skip=skip, limit=limit)


@app.get("/books/{book_id}", response_model=schemas.BookResponse)
def read_book(book_id: int, db: Session = Depends(get_db)):
db_book = crud.get_book_by_id(book_id, db)
if db_book is None:
raise HTTPException(status_code=404, detail="Book not found")
return db_book


@app.delete("/books/{book_id}")
def delete_book(book_id: int, db: Session = Depends(get_db)):
return crud.delete_book(book_id, db)
24 changes: 24 additions & 0 deletions models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from sqlalchemy import String, ForeignKey, Date
from sqlalchemy.orm import relationship, Mapped, mapped_column
from datetime import date
from database import Base


class Author(Base):
__tablename__ = "authors"

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


class Book(Base):
__tablename__ = "books"

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


class BookBase(BaseModel):
title: str
summary: str
publication_date: date


class BookCreate(BookBase):
pass


class BookResponse(BookBase):
id: int
author_id: int

model_config = ConfigDict(from_attributes=True)


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)