问题
I'm getting an ActiveRecord::AssociationTypeMismatch error on my self join in Rails 5 that I can't figure out how to fix.
It's a simple rails app where a user can share a quote by an Artist (such as David Bowie) about another Artist (such as Lou Reed). So, a quote might look like this:
Quote Topic: David Bowie Content: "He was a master." Speaker: Lou Reed
I have a Quote model and an Artist model and the Topics and Speakers are defined as self joins on the Artist model.
Here are the models:
class Artist < ApplicationRecord
default_scope -> { order(name: :asc) }
belongs_to :user
has_many :spoken_quotes, class_name: "Quote", foreign_key: :speaker_id
has_many :topic_quotes, class_name: "Quote", foreign_key: :topic_id
validates :user_id, presence: true
validates :name, presence: true, length: { maximum: 60 },
uniqueness: { case_sensitive: false }
end
class Quote < ApplicationRecord
default_scope -> { order(created_at: :desc) }
belongs_to :user
belongs_to :speaker, class_name: "Artist"
belongs_to :topic, class_name: "Artist"
validates :speaker, uniqueness: {scope: :topic}
validates :topic, uniqueness: {scope: :speaker}
validates :user_id, presence: true
validates :content, presence: true, length: { maximum: 1200 }
validates :source, presence: true, length: { maximum: 60 }
end
Here's the database schema:
create_table "artists", force: :cascade do |t|
t.string "name"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["user_id", "created_at"], name: "index_artists_on_user_id_and_created_at"
t.index ["user_id"], name: "index_artists_on_user_id"
end
create_table "quotes", force: :cascade do |t|
t.integer "user_id"
t.integer "artist_id"
t.text "content"
t.string "source"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["artist_id", "created_at"], name: "index_quotes_on_artist_id_and_created_at"
t.index ["artist_id"], name: "index_quotes_on_artist_id"
t.index ["user_id", "created_at"], name: "index_quotes_on_user_id_and_created_at"
t.index ["user_id"], name: "index_quotes_on_user_id"
end
Here's the relevant code from my Quotes Controller:
def create
@quote = current_user.quotes.build(quote_params)
if @quote.save
flash[:success] = "Quote created!"
redirect_to root_url
else
@feed_items = []
render 'static_pages/home'
end
end
def quote_params
params.require(:quote).permit(:content, :source, :topic, :artist_id)
end
And the dropdown for the Topic of a Quote (which is an Artist) on the new Quote form:
<%= f.collection_select :topic, Artist.all, :id, :name %>
The dropdown looks fine and appears to be creating the association correctly, but when I submit the form I get the following error:
Artist(#70317289606580) expected, got "15" which is an instance of String(#70317259521760)
And the error message highlights the first line in the create action: @quote = current_user.quotes.build(quote_params)
Am I defining my params wrong? What is wrong about my create action to cause this error. I can't seem to figure it out after researching it a bunch and trying various solutions.
回答1:
Lee -
Your Quote and Artist models look OK. Your schema, however, is wrong. It should look like:
create_table "artists", force: :cascade do |t|
t.integer "user_id"
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "quotes", force: :cascade do |t|
t.integer "user_id"
t.integer "speaker_id"
t.integer "topic_id"
t.text "content"
t.string "source"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
Note speaker_id and topic_id instead of artist_id.
I'd need to see your stack trace to see what might be wrong with how you have other things set up.
BTW, have you fixed your params whitelist? This is wrong:
def quote_params
params.require(:quote).permit(:content, :source, :topic, :artist_id)
end
Since your params look like:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"7xXgP3T1ZyxVhnr9TtBxeuYSRLBiuX01JSkQ4m4rN9pBS1W0iW6TJtsS7KyvunpCIZFiFltmdEwZGIYqsnxbyw==", "quote"=>{"topic_id"=>"2", "speaker_id"=>"1", "content"=>"asdfadsf", "source"=>"http://fuzz.com"}, "commit"=>"Post"}
It should be:
def quote_params
params.require(:quote).permit(:content, :source, :topic_id, :speaker_id)
end
As a shot in the dark, try changing:
validates :speaker, uniqueness: {scope: :topic}
validates :topic, uniqueness: {scope: :speaker}
To:
validates :speaker_id, uniqueness: {scope: :topic_id}
validates :topic_id, uniqueness: {scope: :speaker_id}
I'll update with explanation if that's the problem.
回答2:
Try to change your select with:
<%= f.collection_select :topic_id, Artist.all, :id, :name %>
and permit topic_id with on your controller:
params.require(:quote).permit(:content, :source, :topic_id, :artist_id)
You can pass on object as an argument for belongs_to association in rails c:
Quote.new(topic: Artist.first)
and Rails'll do rest, but you can't pass an object via http request, but instead you should pass an object's id.
Updated
I'm confused about how do you want to bind Quote with speaker(Artist) and topic(Artist) having the artist_id column on quotes table only? It seems you should have different columns to store these connections. And then you'll be able to use the name of the column for select's name.
来源:https://stackoverflow.com/questions/48334107/activerecordassociationtypemismatch-in-controllercreate-on-dropdown-select-fo