: From Installation to Implementation
Part 4 in an introduction to using – and making the most of – MongoDB

Recap
In part three, we started working on the Fastapi portion of the monthly budgeting app project. To start, we downloaded the necessary module and began implementation. Because MongoDB uses NoSQL, we had to get a little creative. Once pymongo was hooked up and working, we were able to connect to the database. For each collection, we created an endpoint to get each document.
Continuation
Next, we can work on other endpoints to insert and delete. Before we do that, we can also cover how to delete on the Mongodb end, as we previously only covered how to insert to the collection.
At this time, we will not discuss how to update. That will be in the next part. Because MongoDB is NoSQL, updating is a little more complicated. For instance, in the Grocery collection, there is a nested document of items. Updating a nested document, especially with no ID, makes it more difficult. To further discuss what to do, we will use a separate part for this. For now, we will focus on simply the insert and the delete.
Another note to address is that we sent up Entertainment with validation requirements, mainly being that certain variable types were required. One such type was a double. Unfortunately, pydantic supports float, but not double. So, for this, please note that I dropped the Entertainment collection and recreated it without any validation.
For testing, again we will be using Postman and uvicorn to run the python application. Now we have a little background and direction, we can get started by opening MongoDB. I will skip this step, as we have discussed this in the previous parts.
Deleting in MongoDB
For practice, we will need data to be able to delete. No worries, we can insert some sample data into our Grocery collection.

Next, because that was sample data, we will need to delete that document from the Grocery collection. That way, we can use it for testing the API portion.
To delete, we use the keyword "remove":
db.Grocery.remove({_id:ObjectId("<IdHere>")})

Insert Endpoint for Grocery
To get started, we will need to first use a basic class to plot our fields. This will take place in the app.py file and uses pydantic. In the imports, add the following line:
from pydantic import BaseModel
Next, we will need to declare the classes. It should occur after the app declarations but before any endpoints. The first class we will need is an Item class, which will later be embedded as a list in the Grocery class.
class Item(BaseModel):
ProductName: str
UnitPrice: float
Quantity: int
With this completed, we can create the Grocery class:
class Grocery(BaseModel):
Location: str
Total: float
Coupon: float = None
Giftcard: float = None
User: str
CreatedDate: str
TransactionDate: str
Items: List[Item]
Note that the "None" value allows nothing to be passed in the JSON object. Now we can create the endpoint. It will be the same address as the first grocery endpoint but will use a "post" instead of a "get". We will also need to send a Grocery dictionary.
@app.post("/receiptsapi/v1/grocery")
async def AddGrocery(grocery: Grocery):
groceries = await storage.add_groceries(grocery.dict())
return groceries
Next, we will need to work on the add_groceries() function in the storage.py file. To start, we are going to use the HTTPException to raise any errors we may have. We will also need to declare an ObjectId in order to insert and delete from MongoDB, so we will need to add two imports after our pymongo import but before we declare the pymongo client:
from fastapi import HTTPException
from bson.objectid import ObjectId
Like other FastAPI inserts, we will need a "try-except" around the insert, as well as a status return with a descriptive message. I have written on how to do this in some of my other blogs. However, the difference that because pymongo uses a JSON object, we do not need to place the dictionary into an object. We can simply add the dictionary:
async def add_groceries(grocery):
try:
db.Grocery.insert(grocery)
client.close()
return {"response": "User inserted: %s" %(grocery)}
except Exception as e:
client.close()
raise HTTPException(status_code = 404, detail = e)
For the testing portion, add the JSON object you want to insert in the body:

Insert Endpoint for Emergency
Like the Grocery insert, the Emergency class will need to be set up first:
class Emergency(BaseModel):
Total: float
CreatedDate: str
TransactionDate: str
Reason: str = None
Next, we will add the endpoint.
@app.post("/receiptsapi/v1/emergency")
async def AddEmergency(emergency: Emergency):
emergencies = await storage.add_emergencies(emergency.dict())
return emergencies
Now that the endpoint is set up in the app.py file, we will then move onto the storage.py file to work on the add_emergencies function:
async def add_emergencies(emergency):
try:
db.Emergency.insert(emergency)
client.close()
return {"response": "User inserted: %s" %(emergency)}
except Exception as e:
client.close()
raise HTTPException(status_code = 404, detail = e)
Next, simply test in postman.

Insert Endpoint for Entertainment
When setting up the Entertainment collection, there was one mistake made. The item list was not kept standard with the grocery item list. This is both a strength and a pitfall for NoSQL, standardization is not required. We can work around this with a differently named item class:
class EntertainItem(BaseModel):
Name: str
Price: float
Now we can add the Entertainment class:
class Entertainment(BaseModel):
Location: str
Total: float
Giftcard: float = None
Discount: float = None
CreatedDate: str
TransactionDate: str
Items: List[EntertainItem]
Next, we can set up the entertainment endpoint:
@app.post("/receiptsapi/v1/entertainment")
async def AddEntertainment(entertainment: Entertainment):
entertainments = await storage.add_entertainments(entertainment.dict())
return entertainments
Like before, we then move to the storage.py file to flush out the add_entertainments function:
async def add_entertainments(entertainment):
try:
db.Entertainment.insert(entertainment)
client.close()
return {"response": "User inserted: %s" %(entertainment)}
except Exception as e:
client.close()
raise HTTPException(status_code = 404, detail = e)
Now we can test:

Insert Endpoint for Insurance
Fortunately, we had no special cases for this collection, so we did not have to set up any extra classes. We can get away with just the one in the app.py file like before:
class Insurance(BaseModel):
Location: str
Total: float
CreatedDate: str
TransactionDate: str
In the same file, we add the endpoint:
@app.post("/receiptsapi/v1/insurance")
async def AddInsurance(insurance: Insurance):
insurance = await storage.add_insurance(insurance.dict())
return insurance
Next, move to the storage.py file for the add_insurance function:
async def add_insurance(insurance):
try:
db.Insurance.insert(insurance)
client.close()
return {"response": "User inserted: %s" %(insurance)}
except Exception as e:
client.close()
raise HTTPException(status_code = 404, detail = e)
Onto Postman:

Insert Endpoint for Rent
Another simple class, as there are no special cases for the class:
class Rent(BaseModel):
Total: float
Minimum: float = None
Additional: float = None
CreatedDate: str
TransactionDate: str
Now to the endpoint:
@app.post("/receiptsapi/v1/rent")
async def AddRent(rent: Rent):
rent = await storage.add_rent(rent.dict())
return rent
Next, move to storage.py to set up the add_rent function:
async def add_rent(rent):
try:
db.Rent.insert(rent)
client.close()
return {"response": "User inserted: %s" %(rent)}
except Exception as e:
client.close()
raise HTTPException(status_code = 404, detail = e)
To postman for testing:

Insert Endpoint for Utilities
Last endpoint for inserting to our MongoDB. We first set up our class:
class Utility(BaseModel):
Type: str
Total: float
CreatedDate: str
WithdrawalDate: str
Next, add the endpoint:
@app.post("/receiptsapi/v1/utility")
async def AddUtility(utility: Utility):
utilities = await storage.add_utilities(utility.dict())
return utilities
Now to head to storage.py to make the add_utilities function:
async def add_utilities(utility):
try:
db.Utility.insert(utility)
client.close()
return {"response": "User inserted: %s" %(utility)}
except Exception as e:
client.close()
raise HTTPException(status_code = 404, detail = e)
Finally, to Postman for testing:

Delete Endpoint for Grocery
Now that we learned how to insert into the database using pymongo and FastAPI, we can also learn how to delete. For this, we will need to know the id generated. Because that is being returned in our basic "get" statements, we know the GUID that distinguishes the document. We will need to pass that value to the function. To use a delete, instead of "post" we will use "delete".
@app.delete("/receiptsapi/v1/grocery/{id}")
async def DeleteGrocery(id: str):
groceries = await storage.remove_grocery(id)
return groceries
In the remove_grocery function, we will need to declare the string Id as an ObjectId. After using the delete_one function, all other structures in the "try-except" will be like inserting:
async def remove_grocery(groceryId):
try:
db.Grocery.delete_one({"_id": ObjectId(groceryId)})
client.close()
return {"response": "User inserted: %s" %(groceryId)}
except Exception as e:
client.close()
raise HTTPException(status_code = 404, detail = e)
In Postman, we will need to send the string version of the Id. Which returns this result:

Delete Endpoint for Emergency
With one successful delete endpoint, we will now move onto the Emergency endpoint:
@app.delete("/receiptsapi/v1/emergency/{id}")
async def DeleteEmergency(id: str):
emergencies = await storage.remove_emergency(id)
return emergencies
Now, we move onto the storage.py file:
async def remove_emergency(emergencyId):
try:
db.Emergency.delete_one({"_id": ObjectId(emergencyId)})
client.close()
return {"response": "User inserted: %s" %( emergencyId)}
except Exception as e:
client.close()
raise HTTPException(status_code = 404, detail = e)
Knowing what we know now about testing, use the "get" to grab the Id of what was recently inserted, and use it to delete:

Delete Endpoint for Entertainment
Once again, these tasks will become repetitive as they are similar other than variable names. So, first, we work in the app.py file:
@app.delete("/receiptsapi/v1/entertainment/{id}")
async def DeleteEntertainment(id: str):
entertainment = await storage.remove_entertainment(id)
return entertainment
Next, we move to the storage.py file:
async def remove_entertainment(entertainmentId):
try:
db.Entertainment.delete_one({"_id": ObjectId(entertainmentId)})
client.close()
return {"response": "User inserted: %s" %( entertainmentId)}
except Exception as e:
client.close()
raise HTTPException(status_code = 404, detail = e)
Now, we move to Postman:

Delete Endpoint for Insurance
Halfway there. Set up the DeleteInsurance endpoint in the app.py file first:
@app.delete("/receiptsapi/v1/insurance/{id}")
async def DeleteInsruance(id: str):
insurance = await storage.remove_insurance(id)
return insurance
Now we do the storage.py portion:
async def remove_insurance(insuranceId):
try:
db.Insurance.delete_one({"_id": ObjectId(insuranceId)})
client.close()
return {"response": "User inserted: %s" % (insuranceId)}
except Exception as e:
client.close()
raise HTTPException(status_code = 404, detail = e)
And now to Postman:

Delete Endpoint for Rent
We are nearly there. Now, we will set up the delete Rent endpoint:
@app.delete("/receiptsapi/v1/rent/{id}")
async def DeleteRent(id: str):
rent = await storage.remove_rent(id)
return rent
You know the drill, moving to the storage.py file now:
async def remove_rent(rentId):
try:
db.Rent.delete_one({"_id": ObjectId(rentId)})
client.close()
return {"response": "User inserted: %s" % (rentId)}
except Exception as e:
client.close()
raise HTTPException(status_code = 404, detail = e)
Onto Postman:

Delete Endpoint for Utility
Onto our last endpoint. Like the others, start in the app.py file:
@app.delete("/receiptsapi/v1/utility/{id}")
async def DeleteUtility(id: str):
utilities = await storage.remove_utility(id)
return utilities
And moving to the last portion to the storage.py file:
async def remove_utility(utilityId):
try:
db.Utility.delete_one({"_id": ObjectId(utilityId)})
client.close()
return {"response": "User inserted: %s" % (utilityId)}
except Exception as e:
client.close()
raise HTTPException(status_code = 404, detail = e)
Now for the final Postman test:

Conclusion
First, we learned how to delete documents in MongoDB. We decided not to cover the update portion, as the FastAPI version will be a little more complicated with the embedded/nested documents. Instead, we are saving that for next time.
After learning the MongoDB portion, we moved onto setting up the FastAPI portions. First, we worked on inserting. We learned to use pydantic like a regular FastAPI application, but without having to set up an object in the insert function. Instead, the object is simply passed. The HTTPException, as discussed in a different blog of mine, was used to catch any errors. I also dropped the Entertainment collection and readded it to not include any validation requirements, as pydantic does not use doubles, instead of using floats.
After inserting, we moved onto deleting from the database. To do this, we had to get an Id value, but also convert it to an ObjectId format. We also covered how to correctly pass the id through the endpoint.
We are now one step closer to getting the Monthly Budgeting Project up-and-running. But for now, we will still need to discuss how to update existing documents. Until next time, cheers!
| Sign up to join my mailing list here.
|References
How to retrieve MongoDb collection validator rules?
db.collection.remove() – MongoDB Manual