Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
11 changes: 4 additions & 7 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ share/python-wheels/
MANIFEST

# PyInstaller
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 create_book helper uses models.Book(**book.dict(), author_id=author_id), which is fine now because your Book schemas include summary and publication_date to match the ORM model. Keep in mind this approach relies on schema field names matching model attributes; if you later change schema names consider explicit mapping instead to avoid unexpected kwargs.

# 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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Consider also adding status_code=201 to the create-book endpoint so the API signals successful creation consistently.

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -128,3 +122,6 @@ dmypy.json
# Pyre type checker
.pyre/
.idea/

# SQLite database for FastAPI project
library.db
Empty file added app/__init__.py
Empty file.
34 changes: 34 additions & 0 deletions app/crud.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
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, 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)
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()
44 changes: 44 additions & 0 deletions app/main.py
Original file line number Diff line number Diff line change
@@ -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:
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)):
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)
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_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)
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).

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

Choose a reason for hiding this comment

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

Because the DB model includes publication_date, add that field to your Book schemas (e.g. publication_date: Optional[date] = None) and import date from datetime at the top of this file. Without this the create-book flow cannot provide the model's required attribute.


# ---------- Book Schemas ----------

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

model_config = {"from_attributes": True}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

You use model_config = {"from_attributes": True} which is the Pydantic v2 way to allow parsing ORM objects. If your environment uses Pydantic v1 you must instead set class Config: orm_mode = True on the Pydantic models. Confirm the Pydantic version and adjust accordingly so FastAPI can return SQLAlchemy ORM objects via these schemas.

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 enables Pydantic ORM-mode using Pydantic v2 (model_config = {"from_attributes": True}). Confirm your runtime uses Pydantic v2. If it uses Pydantic v1, add class Config: orm_mode = True to the Pydantic models (or pin Pydantic to v2). Also if you switch to Pydantic v2, update CRUD usage to model_dump() where appropriate — book.dict() is a v1 idiom and may not be the recommended approach in v2.



# ---------- 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}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Same Pydantic-compatibility note for the Author schema: model_config = {"from_attributes": True} is v2-style. If you run under Pydantic v1 change this to class Config: orm_mode = True to ensure ORM objects are serialized correctly.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Same as above for the Author schema: this model_config line is Pydantic v2-specific. Ensure the runtime Pydantic version is compatible or add class Config: orm_mode = True so FastAPI can serialize ORM objects when returning Author instances.

Binary file added requirements.txt
Binary file not shown.