Skip to content

Commit

Permalink
completed api for book-store-management (#144)
Browse files Browse the repository at this point in the history
# Pull Request for PyVerse 💡


## Issue Title : <API for book-store-management>

- **Info about the related issue (Aim of the project)** : To create an
api for book-store-management
- **Name:** Ankit Ojha
- **GitHub ID:** ojhankit
- **Email ID:** <ojha27ankit@gmail.com>
- **Idenitfy yourself: (Mention in which program you are contributing
in. Eg. For a WoB 2024 participant it's, `WoB Participant`)** <!--
Mention in which program you are contributing -->
gssoc-ext

<!-- Mention the following details and these are mandatory -->

Closes: #108 

### Describe the add-ons or changes you've made 📃
create Book model added CRUD operation for 
creating book
reading list of book
updating book details
deleting book

added comments ,requirements file and how to get started with this
project
## Type of change ☑️

What sort of change have you made:
<!--
Example how to mark a checkbox:-
- [x] My code follows the code style of this project.
-->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Code style update (formatting, local variables)
- [ ] Breaking change (fix or feature that would cause existing
functionality to not work as expected)
- [ ] This change requires a documentation update

## How Has This Been Tested? ⚙️

Tested endpoints using Postman

## Checklist: ☑️
<!--
Example how to mark a checkbox:-
- [x] My code follows the code style of this project.
-->
- [x] My code follows the guidelines of this project.
- [x] I have performed a self-review of my own code.
- [x] I have commented my code, particularly wherever it was hard to
understand.
- [x] I have made corresponding changes to the documentation.
- [x] My changes generate no new warnings.
- [x] I have added things that prove my fix is effective or that my
feature works.
- [x] Any dependent changes have been merged and published in downstream
modules.
  • Loading branch information
UTSAVS26 authored Oct 9, 2024
2 parents 41e1395 + f8cd375 commit 0ff9ddd
Show file tree
Hide file tree
Showing 6 changed files with 276 additions and 0 deletions.
2 changes: 2 additions & 0 deletions api_dev/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
__pycache__
test.db
25 changes: 25 additions & 0 deletions api_dev/ReadME.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
**This Project will be a guide to create a minimalistic API for book store management
using FastAPI.**

To get started with this Project, Read the following:

1. set up virtual environment using python -m venv <name-of-virtual-environment>
2. when environment is created , activate it using <name-of-virtual-environment>/Scripts/activate
3. install dependencies using command pip install -r requirements.txt
4. start coding ,since we have three files here

1. database.py
2. models.py
3. main.py

start coding in this order

5. after finishing code you can run your program with this command
uvicorn main:app --reload
6. http://127.0.0.1:8000 you will get this URL,now you can check your api
7. You can Postman/curl for testing.
8. How to Check your api ? Follow this steps mentioned below
http://127.0.0.1:8000/books/ GET request for retrieving all books
http://127.0.0.1:8000/books/ POST request for adding a new book
http://127.0.0.1:8000/books/1 GET request for retireving book with bookid = 1
http://127.0.0.1:8000/books/1 DELETE request for deleting book with bookid = 1
25 changes: 25 additions & 0 deletions api_dev/database.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
""" imports """
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

""" create_engine is used to establish connection to database
session_maker is like a fcatory that produce new Session object when called
Session are used to interact with database
declarative_base is used to define base class from which our all model classes will inhert
"""

""" here sqlite is used as database for simplicity but one can use mysql ,postgress also
for using mysql following steps need to be done
1.install pymysql (pip install pymysql)
2.create a new schema using mysql workbench
3.mysql+pymysql://root:<your-password>@localhost:3306/<database-name> paste this instead of sqlite:///./test.db
"""
URL_DATABASE = "sqlite:///./test.db"


engine = create_engine(URL_DATABASE)

SessionLocal = sessionmaker(autocommit=False ,autoflush=False ,bind=engine)

Base = declarative_base()
160 changes: 160 additions & 0 deletions api_dev/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
""" imports """
from fastapi import FastAPI ,HTTPException ,Depends ,status
from pydantic import BaseModel
from typing import Annotated, List
import models
from database import engine ,SessionLocal
from sqlalchemy.orm import Session
from datetime import datetime

""" instantiating an object of FastAPI class"""
app = FastAPI()
models.Base.metadata.create_all(bind=engine)

""" Defining class BookBase which is inheriting from pydantic BaseModel
pydantic model are used to define the structure and data validation rules for
incoming and outgoing and data
"""
class BookBase(BaseModel):
title:str
author:str
published_at:datetime
publication:str
pages:int

""" this method monitors the life cycle of database session."""
def get_database():
database = SessionLocal()
try:
yield database
finally:
database.close()

database_dependency = Annotated[Session ,Depends(get_database)]

""" API endpoints """
#On reaching this /books/ with post request create_book() method will be called
@app.post('/books/',status_code=status.HTTP_201_CREATED ,response_model=BookBase)
async def create_book(book:BookBase ,db:database_dependency):
"""
Create a new book entry in the database.
Parameters:
- book (BookBase): An object that contains the data required to create a book. This is validated by the BookBase Pydantic model.
- db (database_dependency): A database session object used to interact with the database.
Returns:
- BookBase: The newly created book object, returned as a response in the format defined by the BookBase model.
Process:
1. Receives book data from the request.
2. Converts the Pydantic `BookBase` object into a dictionary.
3. Creates a new `Book` model instance with the data and adds it to the database session.
4. Commits the changes to persist the new book in the database.
5. Refreshes the instance to update it with any changes made by the database (e.g., the auto-generated `id`).
6. Returns the newly created book to the client.
"""
new_book = models.Book(**book.dict())
db.add(new_book)
db.commit()
db.refresh(new_book)
return new_book
#On reaching this /books/ with get request create_book() method will be called
@app.get('/books/',response_model=List[BookBase])
async def get_all_books(db:database_dependency):
"""
Retrieve all book from database
parameters:
- db (database_dependency): A database session object used to interact with the database.
Returns:
- List[BookBase]: A list of all book objects in the database, returned in the format defined by the BookBase model.
Process:
1. Queries the database to retrieve all book entries using SQLAlchemy.
2. Returns the list of book objects, which FastAPI will serialize into JSON format.
"""
books = db.query(models.Book).all()
return books

@app.get('/books/{book_id}')
async def get_book(book_id:int ,db:database_dependency):
"""
Retrieve Book with given book id
parameters:
- book_id : id of book
- db (database_dependency): A database session object used to interact with the database
Returns:
- book object if it exist
Process:
1. Queries the database to retrive book with given book_id using SQLAlchemy
2. Checks if book exist then return it and if not then raise HTTPException
"""
book = db.query(models.Book).filter(models.Book.id == book_id).first()
if book:
return book
else:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Book not found")

@app.patch('/books/{book_id}', response_model=BookBase)
async def update_book(book_id: int, book_data: BookBase, db: database_dependency):
"""
Update the details of a specific book identified by its ID.
Parameters:
- book_id (int): The ID of the book to be updated.
- book_data (BookBase): An object containing the updated book data.
- db (database_dependency): A database session object used to interact with the database.
Returns:
- BookBase: The updated book object.
Process:
1. Queries the database to find the book with the given book_id.
2. Checks if the book exists; if not, raises an HTTPException with a 404 status code.
3. Updates the book's attributes with the provided data, excluding any unset fields.
4. Commits the changes to the database and refreshes the book object to reflect the latest data.
5. Returns the updated book object.
"""
book = db.query(models.Book).filter(models.Book.id == book_id).first()
if not book:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Book not found")


for key, value in book_data.dict(exclude_unset=True).items():
setattr(book, key, value)

db.commit()
db.refresh(book)
return book


@app.delete('/books/{book_id}', status_code=status.HTTP_204_NO_CONTENT)
async def delete_book(book_id: int, db: database_dependency):
"""
Delete the book with the given ID from the database.
Parameters:
- book_id (int): The ID of the book to be deleted.
- db (database_dependency): A database session object used to interact with the database.
Returns:
- None: Returns a 204 No Content response upon successful deletion.
Process:
1. Queries the database to find the book with the given book_id.
2. Checks if the book exists; if not, raises an HTTPException with a 404 status code.
3. Deletes the book from the database and commits the changes.
4. Returns a success message
"""
book = db.query(models.Book).filter(models.Book.id == book_id).first()
if not book:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Book not found")

db.delete(book)
db.commit()
return {"message": "Book deleted successfully"}
22 changes: 22 additions & 0 deletions api_dev/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
""" imports """
from sqlalchemy import Boolean ,Column ,Integer ,String ,DateTime
from database import Base

""" Column: as the name suggest it is used to create new column in the database
Boolean ,Integer ,String ,Datetime :- these are the datatype which will be stored in the database
"""

"""class Book will be table inside the database and it is inheriting from class Base
__tablename__ : name of the table
id ,title ,author .. these are the columns inside table
"""
class Book(Base):
__tablename__ = 'books'

id = Column(Integer ,primary_key=True ,index=True)
title = Column(String(50) ,unique=True)
author = Column(String(50) ,unique=True)
published_at = Column(DateTime)
publication = Column(String(100))
pages = Column(Integer)

42 changes: 42 additions & 0 deletions api_dev/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
alembic==1.13.2
annotated-types==0.7.0
anyio==4.4.0
certifi==2024.6.2
charset-normalizer==3.3.2
click==8.1.7
colorama==0.4.6
dnspython==2.6.1
email_validator==2.2.0
fastapi==0.111.0
fastapi-cli==0.0.4
greenlet==3.0.3
h11==0.14.0
httpcore==1.0.5
httptools==0.6.1
httpx==0.27.0
idna==3.7
Jinja2==3.1.4
Mako==1.3.5
markdown-it-py==3.0.0
MarkupSafe==2.1.5
mdurl==0.1.2
orjson==3.10.5
pydantic==2.7.4
pydantic_core==2.18.4
Pygments==2.18.0
python-dotenv==1.0.1
python-multipart==0.0.9
PyYAML==6.0.1
requests==2.32.3
rich==13.7.1
shellingham==1.5.4
sniffio==1.3.1
SQLAlchemy==2.0.32
starlette==0.37.2
typer==0.12.3
typing_extensions==4.12.2
ujson==5.10.0
urllib3==2.2.2
uvicorn==0.30.1
watchfiles==0.22.0
websockets==12.0

0 comments on commit 0ff9ddd

Please sign in to comment.