ActiveModel::MissingAttributeError: can't write unknown attribute `user_id`

10,085

You have the wrong migration - instead of adding a user_id to comments you add users.commed.

Oops. It can happen to the best of us.

So first let's create a migration to clean up this mistake:

class RemoveCommedFromUsers < ActiveRecord::Migration
  def change
    remove_column :users, :commed # will also remove the index
  end
end

Of course if the App has not been deployed you could just delete the offending migration and run rake db:reset

So lets create the correct migration

rails g migration AddUserToComments user:belongs_to

Which generates the following migration:

class AddUserToComments < ActiveRecord::Migration
  def change
    add_reference :comments, :user, index: true
  end
end

add_reference creates a index and a foreign key in one sweep.

Share:
10,085
Jonathan Musso
Author by

Jonathan Musso

Motorcycle and Hotrod enthusiast. Full Stack Rails student at Bloc.io.

Updated on June 05, 2022

Comments

  • Jonathan Musso
    Jonathan Musso almost 2 years

    I am trying to implement a "comment" feature as part of my assignment for an project I am building.

    Earlier in the course we created a comment table and had the Faker gem generate fake comments.

    My instructions are as follows:

    Comments must be associated with users, so add a user_id foreign key to the comments table. Remember to add an index to it too;

    Update the User model so you can call user.comments, and the Comment model so you can call comment.user;

    Modify the seeds.rb file to create valid comments when you run db:reset;

    Initially I tried to run my rails generate commands but kept running into this error:

    ▶ rake db:migrate
    == 20150508143445 CreateComments: migrating ===================================
    -- create_table(:comments)
    rake aborted!
    StandardError: An error has occurred, this and all later migrations canceled:
    
    SQLite3::SQLException: table "comments" already exists: CREATE TABLE "comments" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "description" text, "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL) /Users/jon/code/bloccit/db/migrate/20150508143445_create_comments.rb:3:in `change'
    -e:1:in `<main>'
    ActiveRecord::StatementInvalid: SQLite3::SQLException: table "comments" already exists: CREATE TABLE "comments" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "description" text, "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL) 
    /Users/jon/code/bloccit/db/migrate/20150508143445_create_comments.rb:3:in `change'
    -e:1:in `<main>'
    SQLite3::SQLException: table "comments" already exists
    /Users/jon/code/bloccit/db/migrate/20150508143445_create_comments.rb:3:in `change'
    -e:1:in `<main>'
    Tasks: TOP => db:migrate
    (See full trace by running task with --trace)
    

    So I discovered that I needed to delete the old table from the start of the course. I did this in the console.

    ActiveRecord::Base.connection.execute("drop table comments")
    

    It seemed to work. I was then able to run these in terminal.

    ▶ rake db:migrate
    == 20150508143445 CreateComments: migrating   ===================================
    -- create_table(:comments)
       -> 0.0015s
    == 20150508143445 CreateComments: migrated (0.0016s) ==========================
    
    == 20150508152354 DropComments: migrating =====================================
    == 20150508152354 DropComments: migrated (0.0000s) ============================
    
    ▶ rails g migration AddCommentToUsers commed_id:integer:index
      invoke  active_record
      create    db/migrate/20150508155200_add_comment_to_users.rb
    
    ▶ rake db:migrate
    == 20150508155200 AddCommentToUsers: migrating ================================
    -- add_column(:users, :commed_id, :integer)
       -> 0.0010s
    -- add_index(:users, :commed_id)
       -> 0.0012s
    == 20150508155200 AddCommentToUsers: migrated (0.0023s) =======================
    

    models/comment.rb

    class Comment < ActiveRecord::Base
      belongs_to :user
    end
    

    models/user.rb

    class User < ActiveRecord::Base
      # Include default devise modules. Others available are:
      # :confirmable, :lockable, :timeoutable and :omniauthable
      devise :database_authenticatable, :registerable,
             :recoverable, :rememberable, :trackable, :validatable, :confirmable
      has_many :posts
      has_many :comments
    
      # CarrierWave method for attribute functionality
      mount_uploader :avatar, AvatarUploader
    
      # These methods check the role of a user in the database
      def admin?
        role == 'admin'
      end
    
      def moderator?
        role == 'moderator'
      end
    end
    

    migrations/create_comments.rb

    class CreateComments < ActiveRecord::Migration
      def change
        create_table :comments do |t|
          t.text :description
    
          t.timestamps null: false
        end
      end
    end
    

    migrations/add_comment_to_users.rb

    class AddCommentToUsers < ActiveRecord::Migration
      def change
        add_column :users, :commed_id, :integer
        add_index :users, :commed_id
      end
    end
    

    In my seeds.rb I have changed

    # Create Comments
    50.times do 
      Comment.create!(
          # user: users.sample, # we have not yet associated Users with Comments
        post: posts.sample,
        description: Faker::Lorem.paragraph
        )
    end
    

    to

    # Create Comments
    50.times do 
      Comment.create!(
        user: users.sample,
        description: Faker::Lorem.paragraph
        )
    end
    

    Now when I run rake db:reset I get this error.

    ▶ rake db:reset
    -- create_table("advertisements", {:force=>:cascade})
       -> 0.0047s
    -- create_table("answers", {:force=>:cascade})
       -> 0.0010s
    -- add_index("answers", ["question_id"], {:name=>"index_answers_on_question_id"})
       -> 0.0014s
    -- create_table("comments", {:force=>:cascade})
       -> 0.0010s
    -- create_table("posts", {:force=>:cascade})
       -> 0.0010s
    -- add_index("posts", ["topic_id"], {:name=>"index_posts_on_topic_id"})
       -> 0.0010s
    -- add_index("posts", ["user_id"], {:name=>"index_posts_on_user_id"})
       -> 0.0014s
    -- create_table("questions", {:force=>:cascade})
       -> 0.0010s
    -- create_table("summaries", {:force=>:cascade})
       -> 0.0010s
    -- add_index("summaries", ["post_id"], {:name=>"index_summaries_on_post_id"})
       -> 0.0010s
    -- create_table("topics", {:force=>:cascade})
       -> 0.0036s
    -- create_table("users", {:force=>:cascade})
       -> 0.0015s
    -- add_index("users", ["commed_id"], {:name=>"index_users_on_commed_id"})
       -> 0.0009s
    -- add_index("users", ["comment_id"], {:name=>"index_users_on_comment_id"})
       -> 0.0012s
    -- add_index("users", ["email"], {:name=>"index_users_on_email", :unique=>true})
       -> 0.0015s
    -- add_index("users", ["reset_password_token"], {:name=>"index_users_on_reset_password_token", :unique=>true})
       -> 0.0017s
    -- initialize_schema_migrations_table()
       -> 0.0034s
    rake aborted!
    ActiveModel::MissingAttributeError: can't write unknown attribute `user_id`
    /Users/jon/code/Bloccit/db/seeds.rb:45:in `block in <top (required)>'
    /Users/jon/code/Bloccit/db/seeds.rb:44:in `times'
    /Users/jon/code/Bloccit/db/seeds.rb:44:in `<top (required)>'
    -e:1:in `<main>'
    Tasks: TOP => db:setup => db:seed
    (See full trace by running task with --trace)
    

    Here is the full stack trace:

    ActiveModel::MissingAttributeError: can't write unknown attribute `user_id`
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/gems/2.2.0/gems/activerecord-4.2.1/lib/active_record/attribute.rb:138:in `with_value_from_database'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/gems/2.2.0/gems/activerecord-4.2.1/lib/active_record/attribute_set.rb:39:in `write_from_user'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/gems/2.2.0/gems/activerecord-4.2.1/lib/active_record/attribute_methods/write.rb:74:in `write_attribute_with_type_cast'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/gems/2.2.0/gems/activerecord-4.2.1/lib/active_record/attribute_methods/write.rb:56:in `write_attribute'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/gems/2.2.0/gems/activerecord-4.2.1/lib/active_record/attribute_methods/dirty.rb:96:in `write_attribute'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/gems/2.2.0/gems/activerecord-4.2.1/lib/active_record/attribute_methods.rb:373:in `[]='
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/gems/2.2.0/gems/activerecord-4.2.1/lib/active_record/associations/belongs_to_association.rb:83:in `replace_keys'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/gems/2.2.0/gems/activerecord-4.2.1/lib/active_record/associations/belongs_to_association.rb:14:in `replace'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/gems/2.2.0/gems/activerecord-4.2.1/lib/active_record/associations/singular_association.rb:17:in `writer'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/gems/2.2.0/gems/activerecord-4.2.1/lib/active_record/associations/builder/association.rb:123:in `user='
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/gems/2.2.0/gems/activerecord-4.2.1/lib/active_record/attribute_assignment.rb:54:in `public_send'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/gems/2.2.0/gems/activerecord-4.2.1/lib/active_record/attribute_assignment.rb:54:in `_assign_attribute'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/gems/2.2.0/gems/activerecord-4.2.1/lib/active_record/attribute_assignment.rb:41:in `block in assign_attributes'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/gems/2.2.0/gems/activerecord-4.2.1/lib/active_record/attribute_assignment.rb:35:in `each'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/gems/2.2.0/gems/activerecord-4.2.1/lib/active_record/attribute_assignment.rb:35:in `assign_attributes'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/gems/2.2.0/gems/activerecord-4.2.1/lib/active_record/core.rb:559:in `init_attributes'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/gems/2.2.0/gems/activerecord-4.2.1/lib/active_record/core.rb:281:in `initialize'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/gems/2.2.0/gems/activerecord-4.2.1/lib/active_record/inheritance.rb:61:in `new'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/gems/2.2.0/gems/activerecord-4.2.1/lib/active_record/inheritance.rb:61:in `new'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/gems/2.2.0/gems/activerecord-4.2.1/lib/active_record/persistence.rb:50:in `create!'
    /Users/jon/code/Bloccit/db/seeds.rb:45:in `block in <top (required)>'
    /Users/jon/code/Bloccit/db/seeds.rb:44:in `times'
    /Users/jon/code/Bloccit/db/seeds.rb:44:in `<top (required)>'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/gems/2.2.0/gems/activesupport-4.2.1/lib/active_support/dependencies.rb:268:in `load'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/gems/2.2.0/gems/activesupport-4.2.1/lib/active_support/dependencies.rb:268:in `block in load'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/gems/2.2.0/gems/activesupport-4.2.1/lib/active_support/dependencies.rb:240:in `load_dependency'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/gems/2.2.0/gems/activesupport-4.2.1/lib/active_support/dependencies.rb:268:in `load'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/gems/2.2.0/gems/railties-4.2.1/lib/rails/engine.rb:547:in `load_seed'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/gems/2.2.0/gems/activerecord-4.2.1/lib/active_record/tasks/database_tasks.rb:250:in `load_seed'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/gems/2.2.0/gems/activerecord-4.2.1/lib/active_record/railties/databases.rake:180:in `block (2 levels) in <top (required)>'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/rake/task.rb:240:in `call'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/rake/task.rb:240:in `block in execute'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/rake/task.rb:235:in `each'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/rake/task.rb:235:in `execute'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/rake/task.rb:179:in `block in invoke_with_call_chain'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/monitor.rb:211:in `mon_synchronize'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/rake/task.rb:172:in `invoke_with_call_chain'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/rake/task.rb:201:in `block in invoke_prerequisites'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/rake/task.rb:199:in `each'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/rake/task.rb:199:in `invoke_prerequisites'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/rake/task.rb:178:in `block in invoke_with_call_chain'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/monitor.rb:211:in `mon_synchronize'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/rake/task.rb:172:in `invoke_with_call_chain'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/rake/task.rb:165:in `invoke'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/gems/2.2.0/gems/activerecord-4.2.1/lib/active_record/railties/databases.rake:139:in `block (2 levels) in <top (required)>'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/rake/task.rb:240:in `call'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/rake/task.rb:240:in `block in execute'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/rake/task.rb:235:in `each'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/rake/task.rb:235:in `execute'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/rake/task.rb:179:in `block in invoke_with_call_chain'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/monitor.rb:211:in `mon_synchronize'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/rake/task.rb:172:in `invoke_with_call_chain'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/rake/task.rb:165:in `invoke'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/rake/application.rb:150:in `invoke_task'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/rake/application.rb:106:in `block (2 levels) in top_level'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/rake/application.rb:106:in `each'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/rake/application.rb:106:in `block in top_level'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/rake/application.rb:115:in `run_with_threads'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/rake/application.rb:100:in `top_level'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/rake/application.rb:78:in `block in run'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/rake/application.rb:176:in `standard_exception_handling'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/rake/application.rb:75:in `run'
    /Users/jon/code/Bloccit/bin/rake:8:in `<top (required)>'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/gems/2.2.0/gems/activesupport-4.2.1/lib/active_support/dependencies.rb:268:in `load'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/gems/2.2.0/gems/activesupport-4.2.1/lib/active_support/dependencies.rb:268:in `block in load'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/gems/2.2.0/gems/activesupport-4.2.1/lib/active_support/dependencies.rb:240:in `load_dependency'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/gems/2.2.0/gems/activesupport-4.2.1/lib/active_support/dependencies.rb:268:in `load'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'
    /Users/jon/.rvm/rubies/ruby-2.2.1/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'
    -e:1:in `<main>'
    Tasks: TOP => db:setup => db:seed
    

    I am stumped here. I thought by deleting the old table and running the new generate commands + migrating everything would be in order. Apparently this is not the case.

    What am I doing wrong here?

    edited to include line 45 of my seeds.rb

    Comment.create!(
    
    • infused
      infused almost 9 years
      The error is raised on line 45 of your seeds.rb file. What's on that line?
    • Jonathan Musso
      Jonathan Musso almost 9 years
      I edited my post to include that line which is "Comment.create!(
    • Florent Ferry
      Florent Ferry almost 9 years
      You don't add user_id to comment. But you add commed_id to user. So comment have no user_id column. You should migrate AddUserstoComment user_id:integer:index.
  • max
    max almost 9 years
    I totally missed that he had added the column to the wrong table (which you can see in the edit) - but I choose to salvage the answer.
  • Jonathan Musso
    Jonathan Musso almost 9 years
    I have deleted the files in the migration folder and the comment.rb model. If I run ActiveRecord::Base.connection.execute("drop table comments") will I be able to start fresh with your answer?
  • max
    max almost 9 years
    You can run rake db:reset instead. It will drop the database and then run rake db:setup.
  • Jonathan Musso
    Jonathan Musso almost 9 years
    When I try rake db:reset I get this error: ActiveRecord::UnknownAttributeError: unknown attribute 'user' for Comment.
  • max
    max almost 9 years
    Try splitting it up: rake db:drop, rake:db:migrate - make sure the migration is successful. Then run rake db:seed.
  • Jonathan Musso
    Jonathan Musso almost 9 years
    The drop appeared to work, no console output? Migration fails with SQLite3::SQLException: duplicate column name: user_id: ALTER TABLE "comments" ADD "user_id" integer/Users/jon/code/bloccit/db/migrate/20150508170603_add‌​_users_to_comment.rb‌​:3:in `change' so I must change the add_reference :user to :user_id?
  • max
    max almost 9 years