I think I can describe this better in a practical sense. What I am trying to do is associate a child to its parent. Given the actual scenario of what I am doing, right now I have two models owner and property. Owner has many properties and property belongs to a owner. I am able to create a owner and save it to the database, but I am not able to create a property and get it to save to the owner I am assigning it to.
Here is an example from my JSON file to show you what I am trying to accomplish
{
"name": "Minnie Doe",
"phone_number": "2105555555",
"real_estate_agent": "Stephen King",
"properties": [
{
"address": "Oz Palace",
"state": "KS",
"sale_price": "$20000000",
"owner_id": 4
}
]
},
#here are my owner and property models
class Owner < ApplicationRecord
has_many :properties
end
class Property < ApplicationRecord
belongs_to :owner
end
This is a fullstack project I am working on so my frontend is HTML and JS, while my backend is a rails api.
//As I am creating a owner, it is posting to the owner table...........
<div id = "owner_form">
<form action='http://localhost:3000/owners/:id' method='POST'>
<div>
<label for='name'>Owner Name:</label>
<input type='text' id='name' name='name' placeholder="John Doe"/>
</div>
<div>
<label for='phone_number'>Phone Number:</label>
<input type='text' id='phone_number' name='phone_number' placeholder="John Doe"/>
</div>
<div>
<label for='real_estate_agent'>Listing Agent:</label>
<input type='text' id='real_estate_agent' name='real_estate_agent' placeholder="John Doe"/>
</div>
<div>
<input type='submit' id='owner_submit' value='Submit'/>
</div>
</form>
</div>
//Then I am using fetch method to dynamically display the owners names in the select tag down bellow based on the response it gets from the server.
<div id = "listing_form">
<form action='http://localhost:3000/properties/:id' method='POST'>
<div>
<label for='name'>Owner Name:</label>
<select id="sel">
</select>
</div>
<div>
<label for='address'>Street Address:</label>
<input type='text' id='address' name='address' placeholder="123 Elm Street"/>
</div>
<div>
<label for='state'>State:</label><br>
<input type='text' id='state' name='state' placeholder="Texas"/>
</div>
<div>
<label for='sale_price'>Listing Price:</label><br>
<input type='text' id='sale_price' name='sale_price' placeholder="$500,000"/>
</div>
<div>
<input type='submit' id='prop_submit' value='Submit'/>
</div>
</form>
</div>
Here is my javascript code for my owner fetch
let dropdown = document.getElementById('sel');
dropdown.length = 0;
let defaultOption = document.createElement('option');
defaultOption.text = 'Choose owner';
dropdown.add(defaultOption);
dropdown.selectedIndex = 0;
const url = OWNERS_URL;
fetch(url)
.then((resp) =>{
if(resp.status !== 200){
console.warn('Looks like there was a problem. Status Code: ' +
resp.status);
return;
}
resp.json().then((data) =>{
let option;
for (let i = 0; i < data.length; i++) {
option = document.createElement('option');
option.text = data[i].name;
dropdown.add(option);
}
});
}
)
.catch(function(err) {
console.error('Fetch Error -', err);
});
And lastly, here is a part of my property controller showing my "create" action when I am trying to submit from my form and post it the the property table of my database.
def create
property = Property.create(prop_params)
if property.save
render json: property
else
render json: { error: "Couldn't save"}
end
end
private
def prop_params
params.permit(:address, :state, :sale_price, :owner_id, :id, :owner_id => [])
end
When I submit the new created property with the selected owner it keeps returning that its not saving and I know it has something to do with my owner_id param in my property controller, just having a hard time thinking around it. How can I overcome this little issue not being able to save property?
Edit as per request
#these are the params returned when I submit property creation
Parameters: {"address"=>"111 Main st. #400", "state"=>"NT", "sale_price"=>"$1,200,000", "id"=>":id"}
you can try like bellow. I hope it should work.
In your HTML file please make changes like this:
<div id = "listing_form">
<form action='http://localhost:3000/properties/:id' method='POST'>
<div>
<label for='owner_id'>Owner Name:</label>
<select id='owner_id' name='owner_id'></select>
</div>
<div>
<label for='address'>Street Address:</label>
<input type='text' id='address' name='address' placeholder="123 Elm Street"/>
</div>
<div>
<label for='state'>State:</label><br>
<input type='text' id='state' name='state' placeholder="Texas"/>
</div>
<div>
<label for='sale_price'>Listing Price:</label><br>
<input type='text' id='sale_price' name='sale_price' placeholder="$500,000"/>
</div>
<div>
<input type='submit' id='prop_submit' value='Submit'/>
</div>
</form>
</div>
In your JS file make changes like this:
let dropdown = document.getElementById('owner_id');
dropdown.length = 0;
let defaultOption = document.createElement('option');
defaultOption.text = 'Choose owner';
defaultOption.value= '';
dropdown.add(defaultOption);
const url = OWNERS_URL;
fetch(url)
.then((resp) => {
if (resp.status !== 200) {
console.warn('Looks like there was a problem. Status Code: ' + resp.status);
return;
}
resp.json().then((data) => {
let option;
for (let i = 0; i < data.length; i++) {
option = document.createElement('option');
option.text = data[i].name;
// I can assume that you are sending id of owners also. If not then please send.
option.value = data[i].id;
dropdown.add(option);
}
});
})
.catch(function(err) {
console.error('Fetch Error -', err);
});
In your controller please make changes:
private
def prop_params
params.permit(:address, :state, :sale_price, :owner_id)
end
You have added /:id
in form action URL. I think this is not needed. You can try without /:id
.
From the above, it looks like you are posting the form with the :id
literal instead of actually passing the ID of the property, generally you would only do that when the entity already exists, for example doing a PUT request to replace/update a resource with a specific ID.
(The literal :id
is used in the routing engine to bind a path parameter to a parameter in the controller)
POST requests would just send the form data to /owners
if you want to create an owner, /properties
if you want to create a property or /owners/1234/properties
if you wanted to create properties on a specific existing owner if you are using nested routing. https://guides.rubyonrails.org/routing.html#nested-resources
To create the resource you would set the form to:
<form action='http://localhost:3000/properties/' method='POST'>
Alternatively, you can use the rails form templating helpers to do this in a cleaner way if you allow nested attributes on the model.
By editing the owner model:
class Owner < ApplicationRecord
has_many :properties
accepts_nested_attributes_for :properties
end
The controller permitted params:
params.require(:owner).permit(:name, :phone_number, :real_estate_agent, properties_attributes: [:address, :state, :sale_price])
And the form:
<%= form_for(:owner) do |form_builder| %>
<%= form_builder.label :name %>
<%= form_builder.text_field :name %>
<%= form_builder.label :phone_number %>
<%= form_builder.telephone_field:phone_numer %>
<%= form_builder.label :real_estate_agent %>
<%= form_builder.text_field :real_estate_agent %>
<%= form.fields_for :properties, [Property.new] do |property_builder| %>
<%= form_builder.label :address %>
<%= form_builder.text_field :address %>
<%= form_builder.label :state %>
<%= form_builder.text_field :state %>
<%= form_builder.label :address %>
<%= form_builder.number_field :sale_price %>
<% end %>
<%= form_builder.submit %>
<% end %>
The above form template would submit properties like:
{owner: {name: "myname", phone_number: "123412", real_estate_agent: "my agent", properties_attributes: [{address: "my address", state: "my state", sale_price: 5554232}]}}
And would generate the association that you describe above.
Is there a particular reason to just use the rails API and not use the templating engine too?
I actually fixed it finally thanks to all of your guidance. So the first part of the issue was in my owner serializer which I totally forgot about.
#I forgot to include the ':id' attribute which was part of the reason I was getting a undefined value attribute in my html
class OwnerSerializer < ActiveModel::Serializer
attributes :id, :name, :phone_number, :real_estate_agent
has_many :properties
end
#Based on a suggestion from @Palash Bera earlier, I was not assigning id to the value attribute. After I made my modification the owner serializer then it was working
fetch(OWNERS_URL)
.then((resp) =>{
if(resp.status !== 200){
console.warn('Looks like there was a problem. Status Code: ' +
resp.status);
return;
}
resp.json().then((data) =>{
for (let i = 0; i < data.length; i++) {
let option;
option = document.createElement('option');
option.text = data[i].name;
// debugger
option.value = data[i].id;
dropdown.add(option);
console.log(option)
}
});
}
)
.catch(function(err) {
console.error('Fetch Error -', err);
});
Once I made these modifications....It was doing what I needed to do.
{
"id": 6,
"name": "Batman",
"phone_number": "2105555555",
"real_estate_agent": "Dick Grayson",
"properties": [
{
"address": "Batcave",
"state": "NY",
"sale_price": "Can't afford",
"owner_id": 6
},
{
"address": "1111 street",
"state": "Tx",
"sale_price": "$500,000",
"owner_id": 6
}
]
},
This was an oversight on my part
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.