diff --git a/.gitignore b/.gitignore index 7ed07d08..6fd6931b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,10 @@ __pycache__/ *.py[cod] *$py.class +database.db +database.py +.idea + # C extensions *.so diff --git a/crud.py b/crud.py new file mode 100644 index 00000000..59ef7c40 --- /dev/null +++ b/crud.py @@ -0,0 +1,41 @@ +from sqlalchemy import select +from models import Author, Book +from schemas import AuthorCreate, BookCreate + + +def get_author(db, author_id: int): + result = db.execute(select(Author).where(Author.id == author_id)) + author = result.scalars().first() + return author + +def get_authors(db, skip: int = 0, limit: int = 10): + result = db.execute(select(Author).offset(skip).limit(limit)) + authors = result.scalars().all() + return authors + +def create_author(db, author: AuthorCreate): + db_author = Author(name=author.name, bio=author.bio) + db.add(db_author) + db.commit() + db.refresh(db_author) + return db_author + +def get_books(db, skip: int = 0, limit: int = 10, author_id: int = None): + query = select(Book) + if author_id: + query = query.where(Book.author_id == author_id) + result = db.execute(query.offset(skip).limit(limit)) + books = result.scalars().all() + return books + +def create_book(db, book: BookCreate): + db_book = 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..6ed72fc7 --- /dev/null +++ b/database.py @@ -0,0 +1,14 @@ +from sqlalchemy import create_engine +from sqlalchemy.orm import DeclarativeBase, sessionmaker + +SQLALCHEMY_DATABASE_URL = "sqlite:///database.db" + +engine = create_engine( + SQLALCHEMY_DATABASE_URL, + connect_args={"check_same_thread": False}, +) +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + + +class Base(DeclarativeBase): + pass diff --git a/main.py b/main.py new file mode 100644 index 00000000..df28a760 --- /dev/null +++ b/main.py @@ -0,0 +1,46 @@ +from typing import List +from fastapi import FastAPI, Depends +from sqlalchemy.orm import Session +import crud +from database import SessionLocal, Base, engine +from schemas import AuthorRead, AuthorCreate, BookCreate, BookRead + +Base.metadata.create_all(bind=engine) + +app = FastAPI() + +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() + +@app.get("/") +def root() -> dict: + return {"message": "Hello World"} + +@app.post("/authors/", response_model=AuthorRead) +def create_author(author: AuthorCreate, db: Session = Depends(get_db)): + return crud.create_author(db=db, author=author) + +@app.get("/authors/", response_model=List[AuthorRead]) +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=AuthorRead) +def read_author(author_id: int, db: Session = Depends(get_db)): + return crud.get_author(db, author_id=author_id) + +@app.post("/books/", response_model=BookRead) +def create_book(book: BookCreate, db: Session = Depends(get_db)): + return crud.create_book(db, book=book) + +@app.get("/books/", response_model=List[BookRead]) +def read_books( + skip: int = 0, + limit: int = 10, + db: Session = Depends(get_db), + author_id: int = None, +): + return crud.get_books(db, skip=skip, limit=limit, author_id=author_id) diff --git a/models.py b/models.py new file mode 100644 index 00000000..2752a320 --- /dev/null +++ b/models.py @@ -0,0 +1,25 @@ +import datetime +from typing import List +from sqlalchemy import ForeignKey +from sqlalchemy.orm import Mapped, mapped_column, relationship +from database import Base + + +class Author(Base): + __tablename__ = "authors" + + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] = mapped_column(unique=True) + bio: Mapped[str] = mapped_column() + books: Mapped[List["Book"]] = relationship(back_populates="author") + + +class Book(Base): + __tablename__ = "books" + + id: Mapped[int] = mapped_column(primary_key=True) + title: Mapped[str] = mapped_column() + summary: Mapped[str] = mapped_column() + publication_date: Mapped[datetime.date] = mapped_column() + author_id: Mapped[int] = mapped_column(ForeignKey("authors.id")) + author: Mapped[Author] = relationship(back_populates="books") diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..3797c229 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,16 @@ +annotated-doc==0.0.4 +annotated-types==0.7.0 +anyio==4.13.0 +click==8.3.3 +colorama==0.4.6 +fastapi==0.136.1 +greenlet==3.5.0 +h11==0.16.0 +idna==3.15 +pydantic==2.13.4 +pydantic_core==2.46.4 +SQLAlchemy==2.0.49 +starlette==1.0.0 +typing-inspection==0.4.2 +typing_extensions==4.15.0 +uvicorn==0.47.0 diff --git a/schemas.py b/schemas.py new file mode 100644 index 00000000..3af6ab61 --- /dev/null +++ b/schemas.py @@ -0,0 +1,36 @@ +import datetime +from typing import List +from pydantic import BaseModel + + +class AuthorCreate(BaseModel): + name: str + bio: str + + +class BookRead(BaseModel): + id: int + title: str + summary: str + publication_date: datetime.date + author_id: int + + class Config: + from_attributes = True + + +class AuthorRead(BaseModel): + id: int + name: str + bio: str + books: List[BookRead] + + class Config: + from_attributes = True + + +class BookCreate(BaseModel): + title: str + summary: str + publication_date: datetime.date + author_id: int