ruby-on-railsnested-attributesaccepts-nested-attributes

UPDATED: Rails form with nested_fields and multiple has_one


I have the following models:

class Property < ApplicationRecord

  # Other validations 

  has_one :address
  accepts_nested_attributes_for :address, update_only: true

end 

class Address < ApplicationRecord
  has_one :country
  has_one :state
  has_one :city
  has_one :suburb

  belongs_to :property
end

The relationships for country, state, city and suburb are all linked with a has_many and belongs_to between each other.

The problem is :

On my properties/_form.html.erb file I'm trying to create the address with nested fields_for the :address and on those nested fields using options_from_collection_for_select. Code below:

  <fieldset>
    <div class="form-group">
      <%= form.label :address, "Dirección", class: "col-sm-2 control-label"%>
      <div class="col-sm-9">
          <%= form.fields_for :address do |ff| %>
            <div class="row">
              <div class="col-sm-4">
                <select class="form-control" name="property[address_attributes][country_id]" >
                  <%= options_from_collection_for_select(Country.all, :id, :name) %>
                </select>
              </div>
              <div class="col-sm-4">
                <select class="form-control"  name="property[address_attributes][state_id]" >
                  <%= options_from_collection_for_select(State.all, :id, :name) %>
                </select>
              </div>
              <div class="col-sm-4">
                <select class="form-control"  name="property[address_attributes][city_id]" >
                  <%= options_from_collection_for_select(City.all, :id, :name) %>
                </select>

And getting this error on submit:

submit error

Update 1

So I've changed my form a bit with this:

<%= ff.select :country, options_from_collection_for_select(Country.all, :id, :name) %>

On all of my relationships and now the error I get is:

Country(#70194352893700) expected, got "1" which is an instance of String(#70194311847460)

Country expected got whis is an instance of String

Update 2:

Here is the code on my schema.rb describing my addresses table:

  create_table "addresses", force: :cascade do |t|
    t.string "street"
    t.integer "number"
    t.integer "zip_code"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.integer "property_id"
    t.integer "countries_id"
    t.integer "states_id"
    t.integer "cities_id"
    t.integer "suburbs_id"
    t.index ["cities_id"], name: "index_addresses_on_cities_id"
    t.index ["countries_id"], name: "index_addresses_on_countries_id"
    t.index ["property_id"], name: "index_addresses_on_property_id"
    t.index ["states_id"], name: "index_addresses_on_states_id"
    t.index ["suburbs_id"], name: "index_addresses_on_suburbs_id"
  end

Solution

  • I'd guess that your address table doesn't have a country_id field on it even though your Address model says that there should be a relationship.

    You can verify by looking at db/schema.rb.

    If the address table is missing that column you can do something like this on the command line to add it:

    rails g migration add_country_id_to_addresses country:belongs_to
    

    And then run rake db:migrate.

    There's a little bit of "magic" involved in the instructions above, so here's an explanation.

    rails g migration is telling Rails to generate a database migration.

    add_country_to_addresses is the name of the migration. You could also use AddCountryToAddresses if you're into CamelCase over snake_case. If rails finds _to_* (or To*) at the end of your migration name it can infer the name of the table to generate the migration for. In this case the addresses table.

    country:belongs_to tells Rails that the migration should link the addresses table to the countries table. It will add a country_id column and (depending on your db settings) will generate an index and/or foreign key for the new column.

    Update

    Your db/schema.rb shows that you do not, in fact, have a country_id field on that table. You have a countries_id field.

    To fix this you can generate a blank migration:

    rails g migration rename_countries_id_on_addresses
    

    And then edit the migration to contain:

    change_table :addresses do |t|
      t.rename :countries_id, :country_id
    end
    

    And then run migrations:

    rake db:migrate
    

    You can find more info about changing tables with migrations here: https://guides.rubyonrails.org/active_record_migrations.html#changing-tables