ActiveRecord::AssociationTypeMismatch in Controller#create on dropdown select for a Rails self join

不羁的心 提交于 2019-11-30 20:34:56

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!