简体   繁体   中英

rails migration setting boolean field doesn't work

I have a migration which adds a boolean column and sets value for some of the rows. The new value doesn't get saved to the database when the model is saved. Here is a simplified version of the code:

class AddSmartToStudent
  def change
    add_column :students, :smart, :boolean

    Student.where(grade: 'A').each do |student|
      student.smart = true
      student.save!
    end
   end
end

Thanks in advance!

In your migration, you add a boolean column, and right after that use it in the model. Not sure it is possible - when migration is not ended yet the transaction is not committed. The Student model might not have smart field yet.

As Luis Silva suggests you could use reset_column_information method to refresh info about columns for Student . But the problem is migrations are not for manipulating with data. If you want to change some data it's better to do in a rake task.

If for some reason you HAVE TO do it in migration, you can do it in plain SQL query. For PostgreSQL it will be:

execute "UPDATE students SET smart='t' WHERE grade='A'"

Try to reset the cached information about columns, which will cause them to be reloaded on the next request.

Execute this line before your clause where

Student.reset_column_information

reset_column_information

There are two issues that are clear to me right of the bet.

  1. As stated by others you're trying to use an attribute that you add in that same migration. The safe thing to do is to reset the column information like explained in the answer of Luis Silva .

  2. The second issue has to do with the fact that you use def change where some of the content isn't reversible. Everything in the change method should be reversible. Otherwise def up and def down should be used.

Here are two options that might solve your issue:

  1. Using def up and def down .

     class AddSmartToStudent def up add_column :students, :smart, :boolean Student.reset_column_information Student .where(grade: 'A') .find_each { |student| student.update!(smart: true) } end def down remove_column :students, :smart end end 
  2. Using reversible .

     class AddSmartToStudent def change add_column :students, :smart, :boolean reversible do |change| change.up do Student.reset_column_information Student .where(grade: 'A') .find_each { |student| student.update!(smart: true) } end end end end 

If you don't care about Rails callbacks, validations, etc. you could also use

Student.where(grade: 'A').update_all(smart: true)

as replacement for

Student.where(grade: 'A').find_each { |student| student.update!(smart: true) }

This updates all the records with a single query but doesn't instantiate the records, meaning Rails callbacks, validations, etc. won't run. For more info see update_all .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM