diff --git a/.gitignore b/.gitignore index 7ed07d08..2863e971 100644 --- a/.gitignore +++ b/.gitignore @@ -28,8 +28,6 @@ share/python-wheels/ MANIFEST # PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec @@ -85,13 +83,9 @@ ipython_config.py .python-version # pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. #Pipfile.lock -# PEP 582; used by e.g. github.com/David-OConnor/pyflow +# PEP 582 __pypackages__/ # Celery stuff @@ -128,3 +122,6 @@ dmypy.json # Pyre type checker .pyre/ .idea/ + +# SQLite database for FastAPI project +library.db diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/app/crud.py b/app/crud.py new file mode 100644 index 00000000..d74a6632 --- /dev/null +++ b/app/crud.py @@ -0,0 +1,34 @@ +from sqlalchemy.orm import Session +from app import models, schemas + +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 = 0, limit: int = 10): + return db.query(models.Author).offset(skip).limit(limit).all() + +def create_author(db: Session, author: schemas.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_books(db: Session, skip: int = 0, limit: int = 10): + return db.query(models.Book).offset(skip).limit(limit).all() + +def get_books_by_author(db: Session, author_id: int, skip: int = 0, limit: int = 10): + return ( + db.query(models.Book) + .filter(models.Book.author_id == author_id) + .offset(skip) + .limit(limit) + .all() + ) + +def create_book(db: Session, book: schemas.BookCreate, author_id: int): + db_book = models.Book(**book.dict(), author_id=author_id) + db.add(db_book) + db.commit() + db.refresh(db_book) + return db_book diff --git a/app/database.py b/app/database.py new file mode 100644 index 00000000..3a597cde --- /dev/null +++ b/app/database.py @@ -0,0 +1,11 @@ +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker, declarative_base + +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() diff --git a/app/main.py b/app/main.py new file mode 100644 index 00000000..919e1ee9 --- /dev/null +++ b/app/main.py @@ -0,0 +1,44 @@ +from fastapi import FastAPI, Depends, HTTPException +from sqlalchemy.orm import Session +from typing import Optional +from app import models, schemas, crud +from app.database import engine, SessionLocal, Base + +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.Author) +def create_author(author: schemas.AuthorCreate, db: Session = Depends(get_db)): + return crud.create_author(db=db, author=author) + +@app.get("/authors/", response_model=list[schemas.Author]) +def read_authors(skip: int = 0, limit: int = 10, db: Session = Depends(get_db)): + return crud.get_authors(db, skip=skip, limit=limit) + +@app.get("/authors/{author_id}", response_model=schemas.Author) +def read_author(author_id: int, db: Session = Depends(get_db)): + db_author = crud.get_author(db, author_id=author_id) + if db_author is None: + raise HTTPException(status_code=404, detail="Author not found") + return db_author + +@app.post("/authors/{author_id}/books/", response_model=schemas.Book) +def create_book_for_author(author_id: int, book: schemas.BookCreate, db: Session = Depends(get_db)): + db_author = crud.get_author(db, author_id=author_id) + if db_author is None: + raise HTTPException(status_code=404, detail="Author not found") + return crud.create_book(db=db, book=book, author_id=author_id) + +@app.get("/books/", response_model=list[schemas.Book]) +def read_books(skip: int = 0, limit: int = 10, author_id: Optional[int] = None, db: Session = Depends(get_db)): + if author_id is not None: + return crud.get_books_by_author(db, author_id=author_id, skip=skip, limit=limit) + return crud.get_books(db, skip=skip, limit=limit) diff --git a/app/models.py b/app/models.py new file mode 100644 index 00000000..ab123f5e --- /dev/null +++ b/app/models.py @@ -0,0 +1,21 @@ +from sqlalchemy import Column, Integer, String, Date, ForeignKey +from sqlalchemy.orm import relationship +from app.database import Base + +class Author(Base): + __tablename__ = "authors" + + id = Column(Integer, primary_key=True, index=True) + name = Column(String, unique=True, index=True) + bio = Column(String) + books = relationship("Book", back_populates="author") + +class Book(Base): + __tablename__ = "books" + + id = Column(Integer, primary_key=True, index=True) + title = Column(String, index=True) + summary = Column(String) + publication_date = Column(Date) + author_id = Column(Integer, ForeignKey("authors.id")) + author = relationship("Author", back_populates="books") diff --git a/app/schemas.py b/app/schemas.py new file mode 100644 index 00000000..533640a7 --- /dev/null +++ b/app/schemas.py @@ -0,0 +1,35 @@ +from typing import List, Optional +from datetime import date +from pydantic import BaseModel, Field + +# ---------- Book Schemas ---------- + +class BookBase(BaseModel): + title: str + summary: Optional[str] = None + publication_date: date + +class BookCreate(BookBase): + pass + +class Book(BookBase): + id: int + author_id: int + + model_config = {"from_attributes": True} + + +# ---------- Author Schemas ---------- + +class AuthorBase(BaseModel): + name: str + bio: Optional[str] = None + +class AuthorCreate(AuthorBase): + pass + +class Author(AuthorBase): + id: int + books: List[Book] = Field(default_factory=list) + + model_config = {"from_attributes": True} diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..b37ae492 Binary files /dev/null and b/requirements.txt differ