diff --git a/crud.py b/crud.py new file mode 100644 index 00000000..33a10c6b --- /dev/null +++ b/crud.py @@ -0,0 +1,65 @@ +from fastapi import Query +from sqlalchemy import select +from sqlalchemy.orm import Session + +import models +from schemas import AuthorCreate, BookCreate + + +def get_all_authors( + db: Session, + skip: int = Query(default=0, ge=0), + limit: int = Query(default=9, ge=1) + ): + + return db.scalars(select(models.Author)).offset(skip).limit(limit).all() + + +def get_author( + db: Session, + author_id: int +): + return db.scalar(select(models.Author).where(models.Author.id == author_id)) + + +def create_author(db: Session, author: AuthorCreate): + db_author = models.Author( + name=author.name, + bio=author.bio, + ) + db.add(db_author) + db.commit() + db.refresh(db_author) + + return db_author + + +def get_all_books(db: Session, + author_id: int | None = None, + skip: int = Query(default=0, ge=0), + limit: int = Query(default=9, ge=1) + ): + queryset = select(models.Book) + + if author_id is not None: + queryset = queryset.join(models.Author).where(models.Author.id == author_id) + + return db.scalars(queryset).offset(skip).limit(limit).all() + + +def get_book(db: Session, book_id: int): + return db.scalar(select(models.Book).where(models.Book.id == book_id)) + + +def create_book(db: Session, book: BookCreate): + db_book = models.Book( + title=book.title, + summary=book.summary, + publication_date=book.publication_date, + author_id=book.author_id, + ) + db.add(db_book) + db.commit() + db.refresh(db_book) + + return db_book diff --git a/database.py b/database.py new file mode 100644 index 00000000..96575332 --- /dev/null +++ b/database.py @@ -0,0 +1,16 @@ +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, echo=True, +) + +SessionLocal = sessionmaker(autocommit=True, + autoflush=False, + bind=engine) + +base = declarative_base() diff --git a/library.db b/library.db new file mode 100644 index 00000000..ccd9432e Binary files /dev/null and b/library.db differ diff --git a/main.py b/main.py new file mode 100644 index 00000000..5b36a441 --- /dev/null +++ b/main.py @@ -0,0 +1,75 @@ +from sqlalchemy.orm import Session + +from fastapi import FastAPI, Depends, HTTPException, Query + +import crud +import schemas +from database import base, engine, SessionLocal + +app = FastAPI() + + +base.metadata.create_all(bind=engine) + + +def get_db() -> Session: + db = SessionLocal() + try: + yield db + finally: + db.close() + + +@app.get("/authors/", response_model=list[schemas.AuthorList]) +def authors_list( + skip: int = Query(default=0, ge=0), + limit: int = Query(default=9, ge=1), + db: Session = Depends(get_db) + ): + return crud.get_all_authors(skip=skip, limit=limit, db=db) + + +@app.get("/authors/{author_id}/", response_model=schemas.AuthorList) +def author_detail( + author_id: int, + db: Session = Depends(get_db) + ): + author = crud.get_author(db=db, author_id=author_id) + + if author is None: + raise HTTPException(status_code=404, detail="Author with this id is not found") + + return author + + +@app.post("/authors/", response_model=schemas.AuthorList) +def author_create( + author: schemas.AuthorCreate, + db: Session = Depends(get_db) +): + return crud.create_author(db=db, author=author) + + +@app.get("/books/", response_model=list[schemas.BookList]) +def books_list( + skip: int = Query(default=0, ge=0), + limit: int = Query(default=9, ge=1), + author_id: int | None = None, + db: Session = Depends(get_db) + ): + return crud.get_all_books(skip=skip, limit=limit, db=db, author_id=author_id) + + +@app.get("/books/{book_id}/", response_model=schemas.BookList) +def book_detail(book_id: int, db: Session = Depends(get_db)): + book = crud.get_book(db=db, book_id=book_id) + + if book is None: + raise HTTPException(status_code=404, detail="Book with this id is not found") + + return book + + +@app.post("/books/", response_model=schemas.BookList) +def create_book(book: schemas.BookCreate, db: Session = Depends(get_db)): + return crud.create_book(db=db, book=book) diff --git a/models.py b/models.py new file mode 100644 index 00000000..9cf7defe --- /dev/null +++ b/models.py @@ -0,0 +1,29 @@ +from datetime import date + +from sqlalchemy import String, ForeignKey, Date + +from database import base +from sqlalchemy.orm import Mapped, mapped_column, relationship + + +class Author(base): + __tablename__ = "author" + + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] = mapped_column(String(255), unique=True) + bio: Mapped[str] = mapped_column(String(511)) + + books: Mapped[list["Book"]] = relationship(back_populates="author") + + +class Book(base): + __tablename__ = "book" + + id: Mapped[int] = mapped_column(primary_key=True) + title: Mapped[str] = mapped_column(String(255)) + summary: Mapped[str] = mapped_column(String(255)) + publication_date: Mapped[date] = mapped_column(Date) + author_id: Mapped[int] = mapped_column(ForeignKey("author.id")) + + author: Mapped["Author"] = relationship(back_populates="books") + diff --git a/schemas.py b/schemas.py new file mode 100644 index 00000000..a13d09f8 --- /dev/null +++ b/schemas.py @@ -0,0 +1,35 @@ +from pydantic import BaseModel, ConfigDict + +from datetime import date + + +class AuthorBase(BaseModel): + name: str + bio: str + + +class AuthorCreate(AuthorBase): + pass + + +class AuthorList(AuthorBase): + id: int + + model_config = ConfigDict(from_attributes=True) + + +class BookBase(BaseModel): + title: str + summary: str + publication_date: date + + +class BookCreate(BookBase): + author_id: int + + +class BookList(BookBase): + id: int + author: AuthorList + + model_config = ConfigDict(from_attributes=True)