The world’s leading publication for data science, AI, and ML professionals.

MongoDB

From Installation to Implementation: Part 6

Photo by Markus Winkler on Unsplash
Photo by Markus Winkler on Unsplash

Previously on MongoDB

In part five, we learned about how to update documents using a few different Mongo commands. We also discussed that due to having embedded documents, it would be a little more difficult to update because of the chance details are overwritten. While updating, we learned we could update every field, or we could update only specific ones. Once we discussed a few different methods of updating, we decided to take a little time to think about how we would want to update our Fastapi portion.

This Time on MongoDB

Having taken a little time, I decided how I want to update our documents. Keep in mind, this is only my solution, and it may not be the only solution nor the best solution. This is only meant to note what I decided to do as I’m learning, so feel free to share any of your ideas in the comments.

Because of the embedded document, we will have to be careful. However, looking at a front-end application, I decided I would want to update all segments, and simply send the whole JSON object for updating. When displayed, the user could decide if they want the field updated, or if it would be replaced with the same data. Sending the entire object would not only allow a dynamic way to update the documents but would also provide for the embedded documents.

Now, for this update to work the way I want, like the INSERT we are using the classes we set up. So if the document is missing any fields, such as with the Grocery document having the optional Coupon or Giftcard, they would be added and set to null. Not ideal, but nothing concerning either. We could always throw logic in our front-end code to not display any NULL values, but having them may actually work to our advantage. It would allow the user to add those fields they missed before without much difficulty. So without further delay, let’s get into the code.

Setting Up The Grocery Update Endpoint

Recall, we have an app.py file and a storage.py file. The app file contains the FastAPI calls, while the storage file deals with the functions and our Mongodb.

To create our update endpoint, we first need to create the function. So first, let’s go to our storage file. When updating in Mongo, we used the update function. The first argument contained a statement similar to a SQL WHERE clause, in which we first chose which document we want to update. If left blank, it would update all. The second argument had the "$set" instruction which is like a SQL SET statement. However, because the fields being will also be contained in a JSON object, we can simply send the entire object instead of needing to declare each and its value. This helps us to grab the entire object.

That being said, while I test with Postman, I am not sending the ID primary key to ensure that it is not accidentally updated. When we send the object in our code, all we need to do is make sure our ObjectID is not editable by the user, and that would solve our issue. For testing purposes, we can leave it off the object we’re sending through Postman.

In addition, we allow an upsert, which means that if for whatever reason a match on ID could not be made, it will insert that document. We shouldn’t need this for our code once we have a front-end, but in case the user forgets to refresh a page or something has been cached, we need to make sure the data won’t just disappear. Knowing all this, here is the function we will add to the storage file:

@app.put("/receiptsapi/v1/grocery/{id}")
async def UpdateGrocery(grocery: Grocery, id: str):
     groceries = await storage.update_groceries(grocery.dict(), id)
     return groceries

Now that we have our code set up, we’re ready to select which document to update. I chose the following:

Selected grocery record.
Selected grocery record.

That is exactly what we will copy into the Postman Body, but without the ObjectID. So let’s make a change. We could change our total amount and also the number of Items in our embedded document list. Now simply send the PUT and make sure the right ID is in the URL.

PUT request response.
PUT request response.

We received a good response back, but just to be sure, let’s go back to a GET to look in the collection.

Updated grocery record.
Updated grocery record.

Looks like our collection updated without any issue. The only problem to note is that the "Coupon" and "Giftcard" fields have been added, but again, this is not an issue because now the user has the opportunity to add in those fields if they so choose. We’re good to go on the next endpoint.

Setting Up The Emergency Update Endpoint

To first begin, let’s go to the storage.py file. Our function will look nearly identical to the update_groceries function, only with different variable names. As you may recall, there were no embedded documents in this collection, yet our call will look identical. That’s why I opted to take this route for the updates; We are using a more dynamic update.

async def update_emergencies(emergency, emergencyId):
     try:
          db.Emergency.update_one({"_id": ObjectId(emergencyId)}, {"$set": emergency}, upsert=True)
          return {"response": "User Updated: %s" % emergencyId}
     except Exception as e:
          client.close()
          raise HTTPException(status_code = 404, detail = e)

Next, we can add to the app.py file and set up our endpoint. Again, it will look the same as the grocery endpoint other than the name changes, but all of our endpoints are the same with few changes.

@app.put("/receiptsapi/v1/emergency/{id}")
async def UpdateEmergency(emergency: Emergency, id: str):
     emergencies = await storage.update_emergencies(emergency.dict(), id)
     return emergencies

Now we view our current emergencies to select which will be updated.

Selected emergency record.
Selected emergency record.

I chose this document. In the reason, we could also add that the higher price was due to the date being a Holiday. That way in the future we could recall why the pricing cost extra for our budget. So let’s throw the ObjectID into the URL, use a PUT to call our endpoint, and then see if the document was updated correctly:

PUT request response.
PUT request response.
Updated emergency record.
Updated emergency record.

Setting Up The Entertainment Update Endpoint

By now I’m sure you’re getting the idea. First, go to the storage file to set up the function:

async def update_entertainment(entertainment, entertainmentId):
     try:
          db.Entertainment.update_one({"_id": ObjectId(entertainmentId)}, {"$set": entertainment}, upsert=True)
          return {"response": "User Updated: %s" % entertainmentId}
     except Exception as e:
          client.close()
          raise HTTPException(status_code = 404, detail = e)

Next, go to the app file to set up the endpoint:

@app.put("/receiptsapi/v1/entertainment/{id}")
async def UpdateEntertainment(entertainment: Entertainment, id: str):
     entertainment = await
storage.update_entertainment(entertainment.dict(), id)
     return entertainment

Now look at the collection and pick a document to update:

Selected entertainment record.
Selected entertainment record.

In Postman, set up the URL, include the ObjectID, then in the Body change around what you would want. For example, let’s use a gift card for this transaction. The gift card could eat the full amount, so our total would be zero. Once run, double-check the updated document.

PUT request response.
PUT request response.
Updated entertainment record.
Updated entertainment record.

Setting Up The Insurance Update Endpoint

First, set up the update function in the storage file:

async def update_insurance(insurance, insuranceId):
     try:
          db.Insurance.update_one({"_id": ObjectId(insuranceId)}, {"$set": insurance}, upsert=True)
          return {"response": "User Updated: %s" % insuranceId}
     except Exception as e:
          client.close()
          raise HTTPException(status_code = 404, detail = e)

Now move onto the app file to create the endpoint:

@app.put("/receiptsapi/v1/insurance/{id}")
async def UpdateInsurance(insurance: Insurance, id: str):
     insurance = await storage.update_insurance(insurance.dict(), id)
     return insurance

Next, select which document to update.

Selected insurance record.
Selected insurance record.

For this update, instead of Progressive, I’ll use Geico, just to show if a simple mistake was made it can be corrected. You know the drill. Time for Postman.

PUT request response.
PUT request response.
Updated insurance record.
Updated insurance record.

Setting Up The Rent Update Endpoint

First, we add our function to the storage file:

async def update_rent(rent, rentId):
     try:
          db.Rent.update_one({"_id": ObjectId(rentId)}, {"$set": rent}, upsert=True)
          return {"response": "User Updated: %s" % rentId}
     except Exception as e:
          client.close()
          raise HTTPException(status_code = 404, detail = e)

Next, add the update endpoint to the app file:

@app.put("/receiptsapi/v1/rent/{id}")
async def UpdateRent(rent: Rent, id: str):
     rent = await storage.update_rent(rent.dict(), id)
     return rent

Finally, select which document you want to update, put the changes in Postman, and monitor what happens.

Selected rent record.
Selected rent record.
PUT request response.
PUT request response.
Updated rent record.
Updated rent record.

Setting Up The Utility Update Endpoint

In our testing, let’s change things up. But first, we have to add our code. Last one. First, add the function to the storage file:

async def update_utility(utility, utilityId):
     try:
          db.Utility.update_one({"_id": ObjectId(utilityId)}, {"$set": utility}, upsert=True)
          return {"response": "User Updated: %s" % utilityId}
     except Exception as e:
          client.close()
          raise HTTPException(status_code = 404, detail = e)

Next, we add our endpoint to the app file:

@app.put("/receiptsapi/v1/utility/{id}")
async def UpdateUtility(utility: Utility, id: str):
     utilities = await storage.update_utility(utility.dict(), id)
     return utilities

After that, select the record to update:

Selected utility record.
Selected utility record.

So in Postman, we’re going to send the ID of only one character off. For example, the "d" at the end will be an "f" instead. Because of the "upsert", it should add in a new record, but let’s test. For our updates, we should change the total, the CreatedDate to something more recent, and the TransactionDate to the next month. Remember, it should be updating the entire record, but our ID is only one character off.

Now when you check, the response says "User Updated", but we expect that because it’s the message we send if successful. However, check the collection again:

Updated utility record.
Updated utility record.

As you can see, the "upsert" allowed the record to be added to the database. If you don’t want that feature, simply set "upsert" to False, and likely you will get an error back when that ID is not found in the collection. But for my purposes, I want that feature left on.

Conclusion

In this part of our MongoDB/FastApi project, we created our Update endpoints for each collection. We discussed what the "upsert" option does, and why I selected to use it for this project.

As far as Mongo and FastApi coding, that was the last part of our receipt tracking project. The only thing left to do is give our project a front-end app. I hope that you’ve learned a little more about MongoDB and even some about FastApi by using the pymongo library. I certainly learned from this experience. To learn more, I’m considering branching out into React for the front-end, as my experience is limited still. It should continue to be an interesting project. Until next time, cheers!

Read all my articles for free with my weekly newsletter, thanks!

Want to read all articles on Medium? Become a Medium member today!


Check out my recent articles:

Organizing My Pictures With MySQL and Python

All Along I’ve Been Using A SQL Antipattern

Create A Photo Editing App With Python

The Lazy Way of Using Python to Search Wikipedia

Building An Old School Text-Based Game With Python


References:

db.collection.update() – MongoDB Manual

How to Update a MongoDB Document in Python


Related Articles