diff --git a/models.py b/models.py index 8ee7611..9b34b72 100644 --- a/models.py +++ b/models.py @@ -3,7 +3,7 @@ from pydantic import BaseModel, Field class Book(BaseModel): - id: str = Field(default_factory=uuid.uuid4, alias="_id") + id: str = Field() title: str = Field(...) author: str = Field(...) synopsis: str = Field(...) @@ -12,13 +12,27 @@ class Config: allow_population_by_field_name = True schema_extra = { "example": { - "_id": "066de609-b04a-4b30-b46c-32537c7f1f6e", + "_id": "6428a5fffe4c6065ecf51cb7", "title": "Don Quixote", "author": "Miguel de Cervantes", "synopsis": "..." } } +class BookCreate(BaseModel): + title: str = Field(...) + author: str = Field(...) + synopsis: str = Field(...) + + class Config: + allow_population_by_field_name = True + schema_extra = { + "example": { + "title": "Don Quixote", + "author": "Miguel de Cervantes", + "synopsis": "..." + } + } class BookUpdate(BaseModel): title: Optional[str] diff --git a/requirements.txt b/requirements.txt index df4ae42..f1ff0b6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ -fastapi[all]==0.75.0 -pydantic==1.9 -pymongo[srv]==3.11.0 -python-dotenv==0.19.2 -pytest==7.0.1 +fastapi[all]==0.94.1 +pydantic==1.10.6 +pymongo[srv]==4.3.3 +python-dotenv==1.0.0 +pytest==7.2.2 \ No newline at end of file diff --git a/routes.py b/routes.py index f6f44e1..270efc5 100644 --- a/routes.py +++ b/routes.py @@ -1,31 +1,41 @@ from fastapi import APIRouter, Body, Request, Response, HTTPException, status from fastapi.encoders import jsonable_encoder from typing import List - -from models import Book, BookUpdate +from bson import ObjectId +from models import Book, BookUpdate, BookCreate router = APIRouter() -@router.post("/", response_description="Create a new book", status_code=status.HTTP_201_CREATED, response_model=Book) -def create_book(request: Request, book: Book = Body(...)): +@router.post("/", response_description="Create a new book", status_code=status.HTTP_201_CREATED) +def create_book(request: Request, book: BookCreate = Body(...)): book = jsonable_encoder(book) + + # verify if book title already exists + if request.app.database["books"].find_one({"title": book["title"]}): + raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=f"Book with title {book['title']} already exists") + + new_book = request.app.database["books"].insert_one(book) created_book = request.app.database["books"].find_one( {"_id": new_book.inserted_id} ) - return created_book + created_book['id'] = str(created_book['_id']) + return Book(**created_book) @router.get("/", response_description="List all books", response_model=List[Book]) def list_books(request: Request): books = list(request.app.database["books"].find(limit=100)) + for book in books: + book['id'] = str(book['_id']) return books @router.get("/{id}", response_description="Get a single book by id", response_model=Book) def find_book(id: str, request: Request): - if (book := request.app.database["books"].find_one({"_id": id})) is not None: + if (book := request.app.database["books"].find_one({"_id": ObjectId(id)})) is not None: + book['id'] = str(book['_id']) return book raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Book with ID {id} not found") @@ -37,15 +47,16 @@ def update_book(id: str, request: Request, book: BookUpdate = Body(...)): if len(book) >= 1: update_result = request.app.database["books"].update_one( - {"_id": id}, {"$set": book} + {"_id": ObjectId(id)}, {"$set": book} ) if update_result.modified_count == 0: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Book with ID {id} not found") if ( - existing_book := request.app.database["books"].find_one({"_id": id}) + existing_book := request.app.database["books"].find_one({"_id": ObjectId(id)}) ) is not None: + existing_book['id'] = str(existing_book['_id']) return existing_book raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Book with ID {id} not found") @@ -53,7 +64,7 @@ def update_book(id: str, request: Request, book: BookUpdate = Body(...)): @router.delete("/{id}", response_description="Delete a book") def delete_book(id: str, request: Request, response: Response): - delete_result = request.app.database["books"].delete_one({"_id": id}) + delete_result = request.app.database["books"].delete_one({"_id": ObjectId(id)}) if delete_result.deleted_count == 1: response.status_code = status.HTTP_204_NO_CONTENT