Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
Empty file added app/__init__.py
Empty file.
28 changes: 28 additions & 0 deletions app/crud.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from sqlalchemy.orm import Session
from app import models, schemas

def get_author(db: Session, author_id: int):
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The app imports schemas and uses Pydantic response_model throughout. Your schemas.py uses model_config = {"from_attributes": True} (Pydantic v2 style). This will only work if the runtime uses Pydantic v2. Please confirm the Pydantic version. If the environment uses Pydantic v1, add an inner class Config: orm_mode = True to the Pydantic models (or pin Pydantic to v2). This is required so FastAPI can serialize ORM objects for the endpoints declared below.

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):
return db.query(models.Book).filter(models.Book.author_id == author_id).all()

def create_book(db: Session, book: schemas.BookCreate, author_id: int):
db_book = models.Book(**book.dict(), author_id=author_id)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In create_book you build the ORM object with models.Book(**book.dict(), author_id=author_id). That is fine only if the BookCreate schema fields exactly match the ORM model (including summary and publication_date). If there's any mismatch (e.g. description instead of summary, or missing publication_date) this will raise a TypeError or omit required fields. Consider either keeping the schema fields aligned or explicitly mapping fields (e.g. models.Book(title=..., summary=..., publication_date=..., author_id=...)) to make the intent explicit and avoid runtime errors.

db.add(db_book)
db.commit()
db.refresh(db_book)
return db_book
11 changes: 11 additions & 0 deletions app/database.py
Original file line number Diff line number Diff line change
@@ -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()
37 changes: 37 additions & 0 deletions app/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy.orm import Session
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:
Comment on lines +10 to +13
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In create_author you commit immediately without handling possible DB integrity errors (for example duplicate name unique constraint). Consider catching sqlalchemy.exc.IntegrityError and returning a controlled 4xx response instead of letting a 500 bubble up.

yield db
finally:
db.close()

@app.post("/authors/", response_model=schemas.Author)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This endpoint (and others using response_model) relies on Pydantic ORM-mode to serialize SQLAlchemy objects. Your schemas use model_config = {"from_attributes": True} (Pydantic v2). Confirm your environment is using Pydantic v2; if it uses Pydantic v1 instead, add class Config: orm_mode = True to the Pydantic models so FastAPI can serialize ORM objects.

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])
Comment on lines +21 to +22
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CRUD helper get_books_by_author does not accept skip and limit parameters, but the requirements expect retrieving books with pagination and filtering by author. Consider adding skip and limit args and applying .offset(skip).limit(limit) to the query so the endpoint can return paginated results.

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")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CRUD helper uses models.Book(**book.dict(), author_id=author_id) which will fail if the Pydantic BookCreate schema keys don't match the SQLAlchemy model attributes. Your SQLAlchemy Book model defines summary and publication_date but the provided Book schema uses description and lacks publication_date. Align the names and fields (either change schema to provide summary and publication_date, or change the model to accept description) or explicitly map fields when creating the model instance (e.g. models.Book(title=book.title, summary=book.description, publication_date=book.publication_date, author_id=author_id)).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line uses **book.dict() to construct the ORM model. If you target Pydantic v2 prefer book.model_dump() for dumping fields; also ensure the Pydantic BookCreate fields exactly match the ORM Book constructor (they appear to match now). If you ever change schema field names, map them explicitly here to avoid unexpected keyword errors.

return db_author

@app.post("/authors/{author_id}/books/", response_model=schemas.Book)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: consider adding status_code=201 to create endpoints (e.g. this POST) so responses follow the usual REST convention for resource creation.

def create_book_for_author(author_id: int, book: schemas.BookCreate, db: Session = Depends(get_db)):
return crud.create_book(db=db, book=book, author_id=author_id)
Comment on lines +34 to +38
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The endpoint that creates a book for a given author doesn't check whether the author exists before creating the book. To meet expected behavior and avoid orphaned references, call crud.get_author and raise HTTP 404 if the author is not found before creating the book.


@app.get("/books/", response_model=list[schemas.Book])
def read
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The books listing endpoint is unfinished — def read is incomplete and causes a syntax error. Implement the endpoint to return a paginated list of books and support filtering by author_id (e.g. signature like def read_books(skip: int = 0, limit: int = 10, author_id: Optional[int] = None, db: Session = Depends(get_db))).

21 changes: 21 additions & 0 deletions app/models.py
Original file line number Diff line number Diff line change
@@ -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)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

publication_date: date is required on BookBase (good — matches the ORM model). Note: your CRUD helper currently calls book.dict() to build the ORM Book. If you adopt Pydantic v2 consider using book.model_dump() in CRUD or otherwise ensure book.dict() remains the intended method for your Pydantic version to avoid subtle incompatibilities.

books = relationship("Book", back_populates="author")
Comment on lines +6 to +11
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Book schema defines description but your SQLAlchemy Book model defines summary (and the schema doesn't include publication_date). This mismatch will cause TypeError: __init__() got an unexpected keyword argument 'description' when the code does models.Book(**book.dict(), author_id=...). Align the names and fields: either rename description -> summary and add publication_date: date to the schema (import date from datetime), or explicitly map schema fields to model fields when creating the Book instance.


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)
Comment on lines +18 to +19
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SQLAlchemy Book model declares summary and publication_date fields. Make sure your Pydantic create/update schemas expose matching fields (names and types). Right now the mismatch between summary (model) and description (schema) will break the CRUD creation path.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sets Pydantic ORM behaviour using Pydantic v2 syntax (model_config = {"from_attributes": True}). Confirm your runtime uses Pydantic v2. If the environment uses Pydantic v1, replace this with an inner class Config: orm_mode = True on the model so FastAPI can serialize SQLAlchemy ORM objects correctly.

author_id = Column(Integer, ForeignKey("authors.id"))
author = relationship("Author", back_populates="books")
Comment on lines +20 to +21
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This helper returns all books for an author without pagination. The task requires listing books with pagination (skip, limit); consider adding skip and limit parameters and applying offset/limit here (or ensure the endpoint uses a CRUD function that supports pagination).

32 changes: 32 additions & 0 deletions app/schemas.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from pydantic import BaseModel
from datetime import date
from typing import List, Optional

class BookBase(BaseModel):
title: str
summary: Optional[str] = None
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name column is declared unique=True which is correct per the requirements, but this means inserts can raise DB integrity errors (duplicate names). Make sure your CRUD create_author catches and handles sqlalchemy.exc.IntegrityError and returns a suitable 4xx response (e.g. 409) instead of letting a 500 be returned.

publication_date: date

class BookCreate(BookBase):
pass

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

class Config:
from_attributes = True # ✅ заміна orm_mode
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue: Config inside Book uses from_attributes = True, which is not a valid Pydantic setting in this form. Fix: For Pydantic v1 use class Config: orm_mode = True. For Pydantic v2 use model_config = {"from_attributes": True} at the model class level. This is required so FastAPI can serialize SQLAlchemy ORM objects to the response model.


class AuthorBase(BaseModel):
name: str
bio: Optional[str] = None

class AuthorCreate(AuthorBase):
pass

class Author(AuthorBase):
id: int
books: List[Book] = []
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a mutable default books: List[Book] = [] can cause shared-state bugs across instances. Fix: Use books: List[Book] = Field(default_factory=list) or books: List[Book] = [] but preferably from pydantic import Field and default_factory=list to create a new list per instance.


class Config:
from_attributes = True # ✅ заміна orm_mode
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue: Config inside Author uses from_attributes = True as well (same problem). Fix: Replace with orm_mode = True (Pydantic v1) or model_config = {"from_attributes": True} (Pydantic v2) so Author ORM objects serialize correctly in responses.

Binary file added requirements.txt
Binary file not shown.