问题
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?
回答1:
This form does not have addressable
id/type fields so unless you explicitly set them or related object in controller getting nil
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
回答2:
You're getting confused with polymorphic associations:
With polymorphic associations, a model can belong to more than one other model, on a single association
This means that ActiveRecord
will automatically assign the addressable_type
& addressableid
of the Address
model, depending on which associated model created it.
For example...
#app/models/profile.rb
class Profile < ActiveRecord::Base
has_many :addresses, as: :addressable
end
#app/models/address.rb
class Address < ActiveRecord::Base
belongs_to :addressable, polymorphic: true
end
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. The addressable_id
& addressable_type
will be assigned automatically on the address
record:
#app/controllers/profiles_controller.rb
class ProfilesController < ApplicationController
def create
@profile = Profile.new profile_params
@profile.save
end
private
def profile_params
params.require(:profile).permit(...)
end
end
--
The main problem you have is that you're getting confused between which model has the polymorphic
element of the association.
They should have the references back to profile or wherever the related model is.
@profile = Profile.find x
@address = @profile.addresses.new address_params ...
This will assign addressable_id
and addressable_type
for the profile
model, allowing you to create addresses on behalf of the profile
.
@address = current_user.addresses.new address_params
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:
@address = Address.create(country: "AU")
@profile = Profile.find 1
@profile.addresses << @address
You could also do:
@profile = Profile.find 1
@profile.addresses.create country: "AU"
--
If you wanted addresses to be "dependent" on others, you'd need to add a parent_id
column, and use acts_as_tree or another hierarchy gem.
来源:https://stackoverflow.com/questions/34552688/rails-4-polymorphic-associations