I am trying to make an app in Rails 4.
I have a profile model and an address model.
the Associations are:
profile.rb
has_many :addresses, as: :addressable
address.rb
belongs_to :addressable, :polymorphic => true
Address is a polymorphic model.
I have an address form which has:
<%= simple_form_for(@address) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<div class="row">
<div class="col-xs-3">
<%= f.input :unit %>
</div>
<div class="col-xs-3 col-xs-offset-1">
<%= f.input :street_number %>
</div>
<div class="col-xs-3 col-xs-offset-1">
<%= f.input :street %>
</div>
</div>
<div class="row">
<div class="col-xs-3">
<%= f.input :building %>
</div>
<div class="col-xs-3 col-xs-offset-1">
<%= f.input :city %>
</div>
<div class="col-xs-3 col-xs-offset-1">
<%= f.input :region %>
</div>
</div>
<div class="row">
<div class="col-xs-3">
<%= f.input :zip %>
</div>
<div class="col-xs-3 col-xs-offset-1">
<%= f.input :country, priority: [ "Australia", "New Zealand", "United Kingdom" ] %>
</div>
</div>
<div class="row">
<div class="col-xs-3">
<%= f.input :main_address %>
</div>
<div class="col-xs-3 col-xs-offset-1">
<%= f.input :project_offsite %>
</div>
</div>
</div>
<div class="form-actions">
<%= f.button :submit, :class => "formsubmit" %>
</div>
<% end %>
I try to create an address and save it. I then check that it is in the rails console. Something is going wrong because the rails console shows this:
#<Address id: 1, unit: "", building: "", street_number: "34", street: "f", city: "sdfasdf", region: "asdf", zip: "2000", country: "GB", main_address: nil, project_offsite: nil, time_zone: nil, latitude: nil, longitude: nil, addressable_id: nil, addressable_type: nil, created_at: "2015-12-30 00:07:00", updated_at: "2015-12-30 00:17:00">
The addressable id and the addressable type are both nil.
They should have the references back to profile or wherever the related model is.
Is there a step missing in setting up this process?
This video (minute 3.40) says rails automatically handles filling out the type and id fields for polymorphic models. https://gorails.com/episodes/comments-with-polymorphic-associations?autoplay=1
When I try to create a new address from the rails console, I type:
Address.create(addressable: Address.first, profile_id: "1", country: "AU")
The console error is:
ActiveRecord::UnknownAttributeError: unknown attribute 'profile_id' for Address
My address table has an addressable_id key and my profile model has has_many :addresses, as: :addressable
I can't understand what causes the error
ANOTHER ATTEMPT
So, from what I gather from the suggestions below, I need to do something to let address know that it belongs to a profile.
I also found this article which sets out an example of what needs to happen in the profiles controller to get things working with polymorphic associations: http://6ftdan.com/allyourdev/2015/02/10/rails-polymorphic-models/
I change my new action in the profiles controller to (include addresses.build):
def new
@profile = Profile.new
@profile.qualifications.build
@profile.visions.build
@profile.personalities.build
@profile.addresses.build
authorize @profile
end
I add nested attributes to my address association in the profile model:
has_many :addresses, as: :addressable
accepts_nested_attributes_for :addresses, reject_if: :all_blank, allow_destroy: true
I change my address form: - from form_for to fields_for; - rename it _address_fields.html.erb; and - open the div tag with:
<div class="nested-fields">
In my profiles form, I add the link to the form partial:
Your addresss
</div>
<%= f.simple_fields_for :addresses do |f| %>
<%= render 'addresses/address_fields', f: f %>
<% end %>
</div>
<div class="row">
<div class="col-md-6">
<%= link_to_add_association 'Add an address', f, :addresses, partial: 'addresses/address_fields' %>
</div>
All of this is as set out in Jeiwan's answer on this post: Rails - Cocoon gem - Nested Forms
Then, when I try all of this, I get this error:
ActiveRecord::RecordNotUnique in ProfilesController#update
PG::UniqueViolation: ERROR: duplicate key value violates unique constraint "index_addresses_on_addressable_type_and_addressable_id" DETAIL: Key (addressable_type, addressable_id)=(0, 1) already exists. : INSERT INTO "addresses" ("unit", "building", "street_number", "street", "city", "region", "zip", "country", "addressable_id", "addressable_type", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12) RETURNING "id"
The error points to the update method in the profiles controller:
def update
respond_to do |format|
if @profile.update(profile_params)
format.html { redirect_to @profile }
format.json { render :show, status: :ok, location: @profile }
else
I found this post which describes a user's similar problem, but before i change the database, I'd like to try and understand what has happened.
Rails auto-assigning id that already exists
Another odd thing is when I look at the last address created in the console, it shows this:
#<Address id: 6, unit: "", building: "", street_number: "34", street: "asdf", city: "asdf", region: "add", zip: "2111", country: "AU", main_address: nil, project_offsite: nil, time_zone: nil, latitude: nil, longitude: nil, addressable_id: 1, addressable_type: 0, created_at: "2016-01-01 22:01:31", updated_at: "2016-01-01 22:01:31">]>
The record now has an addressable_id but does not have an addressable_type. Is there something extra required to populate this field?
You're getting confused with polymorphic associations:
This means that
ActiveRecord
will automatically assign theaddressable_type
&addressableid
of theAddress
model, depending on which associated model created it.For example...
The above means that each time you create a profile, you have the ability to create or assign addresses for it. If you had another model (
users
), you could do the same. Theaddressable_id
&addressable_type
will be assigned automatically on theaddress
record:--
The main problem you have is that you're getting confused between which model has the
polymorphic
element of the association.This will assign
addressable_id
andaddressable_type
for theprofile
model, allowing you to create addresses on behalf of theprofile
.This exact same code would allow you to create an address for a user (same model, same associations etc).
To answer your question, you need to scope your object creation around the associated model:
You could also do:
--
If you wanted addresses to be "dependent" on others, you'd need to add a
parent_id
column, and useacts_as_tree
or another hierarchy gem.This form does not have
addressable
id/type fields so unless you explicitly set them or related object in controller gettingnil
is what is expected.Defining the relation in model only states that objects can be related in this way, but does not set actual values. Actually - rails has a lot of magic in it, but it cannot guess which
profile
is the owner of which address