user284130
user284130

Reputation: 95

Can't update belongs_to relationship in migration in Rails

I have the following model :

Class User::Basic
  belongs_to: group

Class Group
  has_many: users
  class_name: "::User::Basic",
  foreign_key: "group_id"

I also have a User::Admin class, which does not belongs to the group class (group_id equals -1)

I have an existing database where groups do not exist. I am trying to edit the group of basic users through the migrations.

In my migrations I have :

add_column :users, :group_id, :integer, :default => -1  #so that when a group_id is not specified, the user is ranked as admin

# I create groups 1,2,3 and 4 and then :

User.find(21, 3, 8, 17, 16, 18, 14, 19, 23, 25, 26).each do |usr|
  usr.update_attributes(group_id: 2)
end
User.find(11, 13, 20, 9).each do |usr|
  usr.update_attributes(group_id: 1)
end
User.find(15,22).each do |usr|
  usr.update_attributes(group_id: 3)
end
User.find(24).update_attributes(group_id: 4)

But all users still have a group_id of -1 after running the migrations. I tried to run the last bit in a console and it worked, so does anybody knows why this is not working in migrations ?

Upvotes: 0

Views: 452

Answers (2)

Richard Peck
Richard Peck

Reputation: 76784

Console

I personally would not include the data-specific calls in a migration, it's simply not what the migrations are for:

Migrations are a convenient way to alter your database schema over time in a consistent and easy way. They use a Ruby DSL so that you don't have to write SQL by hand, allowing your schema and changes to be database independent.

You can think of each migration as being a new 'version' of the database. A schema starts off with nothing in it, and each migration modifies it to add or remove tables, columns, or entries. Active Record knows how to update your schema along this timeline, bringing it from whatever point it is in the history to the latest version. Active Record will also update your db/schema.rb file to match the up-to-date structure of your database.

I wouldn't mention this if you were using a sweeping statement such as User.all etc, but as you're selecting individual records (with the find method), you'll make your migrations VERY data-specific, which isn't what they're for

If you push this to production, and have different ids, how will it work?

Simply don't perform database requests in your migration

--

I would recommend making your migration specifically for your column:

#db/migrate/your_migration.rb
class YourMigration
   def change
      add_column :users, :group_id, :integer, :default => -1
   end
end

Then once this has run, you'll want to use the rails console to perform the data-specific fixes you want:

rails c

groups = {"1" => [11, 13, 20, 9], "2" => [21, 3, 8, 17, 16, 18, 14, 19, 23, 25, 26], "3" => [15,22], "4" => [24]}

groups.each do |group, users|
  usr = User.find(users)
  usr.update_attributes(group_id: group)
end

Upvotes: 2

RAJ
RAJ

Reputation: 9747

Whenever we run the migrations, rails resets the columns information about the tables, after completion of the execution of migration file.

In your case, you are trying to access the column before the completion of migration file execution. So, you need to call reset_column_information explicitly before operating on the column you have just added from the same migration file.

Here is an example:

def change
  add_column :users, :name, :string

  User.reset_column_information

  User.all.each do |u|
    u.name = 'user name'
    u.save!
  end
end

Upvotes: 1

Related Questions