This week, I completed another phase and project through Flatiron School’s Software Engineering program. In this phase, we’ve focused on building the backend of applications through Ruby, Active Record, and Sinatra.
My project focussed on payment tracking to assist users with saving. To track and categorize payments, I had two, one-to-many model associations:
- Payments belong to Stores
- Stores have many payments
- Payments belong to Categories
- Categories have many payments
Setting up the migrations, models, and controllers came without issue. What did not make sense right away was how to build a Payment, with the above relationships, when a user submits payment information through a form.
I struggled for a full 48 hours to understand how to format my Payment object through Javascript that correctly POSTs to the Sinatra built API and identify the two foreign keys I needed connected.
This blog will cover how I accomplished this task and how Binding.Pry
helped me find a solution when I was stuck.
To start, I will explain my API, Frontend Requests, and the Submission Form for Payments:
The Set Up
As described, a Payment
belongs to a Store
and Category
. Categories & Stores can have many payments. Categories and Stores do not currently tie together.
Here is the Payment database model:
A user can submit a payment through the following React form:
Form Submission to POST was something I had encountered before, but relating a value to a foreign key, when the user will have no concept of the foreign key's ID truly knocked me down.
Tip #1: The JS object, or 'Form Data', as I've called it must include the required key, value pairs the server is expecting on the backend.
This may sound obvious, but as I was expecting a user to input a category or store name, I had assumed the client would send the category/store names and then store_id
and category_id
would be converted server side. This is not the case in my example!
const [formData, setFormData] = useState({
amount: "",
date_paid: "",
description: "",
is_need: false,
store_id: "",
category_id: "",
})
const categoriesList = categories.map((category) => (
<MenuItem key={category.id} value={category.id}>{category.category_type}</MenuItem>
))
<Select
displayEmpty
id="standard-basic"
className={classes.selectEmpty}
name="category_id"
value={formData.category_id}
onChange={handleChange}
inputProps={{ 'aria-label': 'Without label' }}
>
<MenuItem value="" disabled>
Category
</MenuItem>
{categoriesList}
</Select>
In my case, I chose to use a Select element for category and store submission to make it easier for the user and myself. The Categories
display as MenuItems
and that also gives me a chance to store that specific category's id under name="category_id"
.
When a store or category is selected, JS then does the work of inserting the corresponding ID to our FormData
.
function handleChange(event) {
if(event.target.name === "amount"){
setFormData({
...formData,
amount: parseFloat(event.target.value)
})
}
else {
setFormData({
...formData,
[event.target.name]: event.target.value
});
}
}
The Server Side
Where I started to see smoke as a newbie engineer is that my JS object was showing up perfectly in the console.log
output. But I was receiving specific API errors that Payment could not be submitting because the params were empty.
aka. My beautiful work was not coming across correctly in the server.
Tip #2: Use Binding.Pry AND Play With It.
Here was my initial payments_controller
for a POST:
and here is what binding.pry
was providing me when I peeked into Payment:
Nil
!? How could that be when my object looked great before it was sent to server?
A tip from an instructor led me to try viewing the [params :payment] vs. just (params) within IRB. This led me to see that I was submitting data correctly, but I was asking my server to look for params called "payment", which did not exist as a key, value pair in my object. The solution:
post "/payments" do
payment = Payment.new(params)
if payment.save
payment.to_json(include: [:store, :category])
else
{errors: payment.errors.full_messages}.to_json
end
end
Back tracking to just asking for "params" solved my issue (+/- some other tweaks to my code)! How could something so small trip me up for two days?
To put it simply, I was looking for the most complicated reason when binding.pry
could have led me to a simple fix much quicker.
Conclusion:
I still struggled with the rest of my API calls because I was just getting used to Sinatra and Active Record set up. Leaning on binding.pry
on the server side while also still utilizing console.log
on the client end helped make sure the data was matching on both ends, taking less of my time!
Thanks for reading!
Top comments (0)