简体   繁体   中英

I am trying to associate child object literal to a parent object literal

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM