From e8f548ae9092aaea2b0691565cd25c2794bc44ef Mon Sep 17 00:00:00 2001 From: PVS1905 Date: Mon, 6 Apr 2026 17:33:39 +0300 Subject: [PATCH 1/2] Solution --- .flake8 | 7 +++++ .gitignore | 1 - crud.py | 82 ++++++++++++++++++++++++++++++++++++++++++++++++ db/__init__.py | 0 db/database.py | 12 +++++++ db/models.py | 31 ++++++++++++++++++ main.py | 69 ++++++++++++++++++++++++++++++++++++++++ requirements.txt | 4 +++ schemas.py | 37 ++++++++++++++++++++++ 9 files changed, 242 insertions(+), 1 deletion(-) create mode 100644 .flake8 create mode 100644 crud.py create mode 100644 db/__init__.py create mode 100644 db/database.py create mode 100644 db/models.py create mode 100644 main.py create mode 100644 requirements.txt create mode 100644 schemas.py diff --git a/.flake8 b/.flake8 new file mode 100644 index 00000000..9804300a --- /dev/null +++ b/.flake8 @@ -0,0 +1,7 @@ +[flake8] +inline-quotes = " +ignore = E203, E266, W503, ANN002, ANN003, ANN101, ANN102, ANN401, N807, N818, VNE001, F401 +max-line-length = 119 +max-complexity = 18 +select = B,C,E,F,W,T4,B9,ANN,Q0,N8,VNE +exclude = .venv, __pycache__, migrations, alembic \ No newline at end of file diff --git a/.gitignore b/.gitignore index 7ed07d08..c82bb5be 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,6 @@ __pycache__/ # C extensions *.so - # Distribution / packaging .Python build/ diff --git a/crud.py b/crud.py new file mode 100644 index 00000000..d90aff8a --- /dev/null +++ b/crud.py @@ -0,0 +1,82 @@ +from fastapi import HTTPException +from sqlalchemy import select +from sqlalchemy.orm import Session + +import schemas +from db import models + + +def get_all_books( + db: Session, + author_id: int | None = None, + skip: int | None = None, + limit: int | None = None, +) -> list[models.Book]: + + query = db.query(models.Book) + if author_id is None: + query = query.where(models.Book.author_id == author_id) + query = query.limit(limit).offset(skip) + + return list(db.scalars(query)) + + +def get_author_by_id( + db: Session, + author_id: int +) -> models.Author | None: + author = db.execute( + select(models.Author).where(models.Author.id == author_id) + ).scalar_one_or_none() + if not author: + raise HTTPException(status_code=404, detail="Author not found") + return author + + +def create_book( + db: Session, + book: schemas.BookCreateSchema +) -> models.Book: + author = db.execute( + select(models.Author).where(models.Author.id == book.author_id) + ).scalar_one_or_none() + + if not author: + raise HTTPException(status_code=404, detail="Author not found") + 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 + + +def create_author( + db: Session, + author: schemas.AuthorCreateSchema +) -> models.Book: + + 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_authors_list( + db: Session, + skip: int | None = None, + limit: int | None = None, +) -> list[models.Author]: + query = db.query(models.Author).offset(skip).limit(limit) + + return list(db.scalars(query)) diff --git a/db/__init__.py b/db/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/db/database.py b/db/database.py new file mode 100644 index 00000000..f3863814 --- /dev/null +++ b/db/database.py @@ -0,0 +1,12 @@ +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker + +SQLALCHEMY_DATABASE_URL = "sqlite:///./db.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/db/models.py b/db/models.py new file mode 100644 index 00000000..54c4ca1c --- /dev/null +++ b/db/models.py @@ -0,0 +1,31 @@ +import datetime +from sqlalchemy import String, Date, ForeignKey +from sqlalchemy.orm import mapped_column, Mapped, relationship + +from db.database import Base + + +class Author(Base): + __tablename__ = "authors" + + 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(512), nullable=False) + books: Mapped[list["Book"]] = relationship( + "Book", back_populates="author" + ) + + +class Book(Base): + __tablename__ = "books" + + id: Mapped[int] = mapped_column(primary_key=True, index=True) + title: Mapped[str] = mapped_column(String(255), nullable=False) + summary: Mapped[str] = mapped_column(String(255), nullable=False) + publication_date: Mapped[datetime.date] = mapped_column(Date, nullable=False) + author_id: Mapped[int] = mapped_column( + ForeignKey("authors.id"), nullable=False + ) + author: Mapped["Author"] = relationship( + "Author", back_populates="books" + ) diff --git a/main.py b/main.py new file mode 100644 index 00000000..73041fe5 --- /dev/null +++ b/main.py @@ -0,0 +1,69 @@ +from typing import Annotated, Generator +from sqlalchemy import select +from fastapi import FastAPI, Depends, HTTPException +from sqlalchemy.orm import Session +import crud +import schemas +from db.database import SessionLocal +from db.models import Book + +app = FastAPI() + + +def get_db() -> Generator[Session, None, None]: + db = SessionLocal() + + try: + yield db + finally: + db.close() + + +@app.get("/books/", response_model=list[schemas.BookListSchema]) +def read_cheese_types( + db: Annotated[Session, Depends(get_db)], + author_id: int | None = None, + skip: int | None = None, + limit: int | None = None, +): + return crud.get_all_books( + db=db, + author_id=author_id, + skip=skip, + limit=limit + ) + + +@app.post("/books/", response_model=schemas.BookSchemaBase) +def create_book_route( + book: schemas.BookCreateSchema, + db: Session = Depends(get_db) +): + return crud.create_book(db=db, book=book) + + +@app.get("/authors/", response_model=list[schemas.AuthorListSchema]) +def get_authors( + db: Annotated[Session, Depends(get_db)], + skip: int | None = None, + limit: int | None = None, +): + return crud.get_authors_list(db=db, skip=skip, limit=limit) + + +@app.post("/authors/", response_model=schemas.AuthorSchemaBase) +def create_author_route( + author: schemas.AuthorCreateSchema, + db: Session = Depends(get_db) +): + return crud.create_author(db=db, author=author) + + +@app.get("/authors/{author_id}/", response_model=schemas.AuthorListSchema) +def get_authors_by_author_id( + db: Annotated[Session, Depends(get_db)], + author_id: int, +): + return crud.get_author_by_id( + db=db, author_id=author_id + ) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..d78fa26c --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +fastapi +sqlalchemy +alembic +uvicorn \ No newline at end of file diff --git a/schemas.py b/schemas.py new file mode 100644 index 00000000..9267a862 --- /dev/null +++ b/schemas.py @@ -0,0 +1,37 @@ +import datetime +from pydantic import BaseModel + + +class BookSchemaBase(BaseModel): + title: str + summary: str + publication_date: datetime.date + author_id: int + + class Config: + from_attributes = True + + +class BookCreateSchema(BookSchemaBase): + pass + + +class BookListSchema(BookSchemaBase): + id: int + + +class AuthorSchemaBase(BaseModel): + name: str + bio: str + + class Config: + from_attributes = True + + +class AuthorCreateSchema(AuthorSchemaBase): + pass + + +class AuthorListSchema(AuthorSchemaBase): + id: int + books: list[BookListSchema] = [] From 8c6e2cd641e305b2c4822413c7b3560dd3d0fb6a Mon Sep 17 00:00:00 2001 From: PVS1905 Date: Mon, 6 Apr 2026 19:36:47 +0300 Subject: [PATCH 2/2] Solution_fixed --- crud.py | 42 +++++++++++++++++++++--------------------- main.py | 13 ++++++++----- schemas.py | 7 +++++-- 3 files changed, 34 insertions(+), 28 deletions(-) diff --git a/crud.py b/crud.py index d90aff8a..891613f6 100644 --- a/crud.py +++ b/crud.py @@ -1,24 +1,26 @@ from fastapi import HTTPException from sqlalchemy import select from sqlalchemy.orm import Session - +from typing import Sequence import schemas from db import models def get_all_books( - db: Session, - author_id: int | None = None, - skip: int | None = None, - limit: int | None = None, -) -> list[models.Book]: - - query = db.query(models.Book) - if author_id is None: + db: Session, + author_id: int | None = None, + skip: int = 0, + limit: int = 10, +) -> Sequence[models.Book]: + + query = select(models.Book) + + if author_id is not None: query = query.where(models.Book.author_id == author_id) - query = query.limit(limit).offset(skip) - return list(db.scalars(query)) + query = query.offset(skip).limit(limit) + + return db.scalars(query).all() def get_author_by_id( @@ -58,8 +60,8 @@ def create_book( def create_author( db: Session, - author: schemas.AuthorCreateSchema -) -> models.Book: + author: schemas.AuthorSchemaBase +) -> models.Author: db_author = models.Author( name=author.name, @@ -72,11 +74,9 @@ def create_author( return db_author -def get_authors_list( - db: Session, - skip: int | None = None, - limit: int | None = None, -) -> list[models.Author]: - query = db.query(models.Author).offset(skip).limit(limit) - - return list(db.scalars(query)) +def get_authors_list(db, skip: int, limit: int) -> list[models.Author]: + return db.scalars( + select(models.Author) + .offset(skip) + .limit(limit) + ).all() diff --git a/main.py b/main.py index 73041fe5..ade388b6 100644 --- a/main.py +++ b/main.py @@ -1,13 +1,16 @@ from typing import Annotated, Generator -from sqlalchemy import select -from fastapi import FastAPI, Depends, HTTPException +from fastapi import Depends from sqlalchemy.orm import Session import crud import schemas from db.database import SessionLocal -from db.models import Book +from fastapi import FastAPI +from db.database import engine +from db.models import Base + app = FastAPI() +Base.metadata.create_all(bind=engine) def get_db() -> Generator[Session, None, None]: @@ -51,9 +54,9 @@ def get_authors( return crud.get_authors_list(db=db, skip=skip, limit=limit) -@app.post("/authors/", response_model=schemas.AuthorSchemaBase) +@app.post("/authors/", response_model=schemas.AuthorSchema) def create_author_route( - author: schemas.AuthorCreateSchema, + author: schemas.AuthorSchemaBase, db: Session = Depends(get_db) ): return crud.create_author(db=db, author=author) diff --git a/schemas.py b/schemas.py index 9267a862..6d3c75df 100644 --- a/schemas.py +++ b/schemas.py @@ -28,8 +28,11 @@ class Config: from_attributes = True -class AuthorCreateSchema(AuthorSchemaBase): - pass +class AuthorSchema(AuthorSchemaBase): + id: int + + class Config: + from_attributes = True class AuthorListSchema(AuthorSchemaBase):