I've got a very simple migration that adds a single boolean column:
class AddMuteToPreferences < ActiveRecord::Migration
def self.up
add_column :preferences, :mute_audio, :boolean, :default => false
end
def self.down
remove_column :preferences, :mute_audio
end
end
I run the migration:
== 81 AddMuteToPreferences: migrating =========================================
-- add_column(:preferences, :mute_audio, :boolean, {:default=>false})
-> 1.9043s
== 81 AddMuteToPreferences: migrated (1.9047s) ================================
Looks peachy, right? But, for some reason, there's still no mute_audio column in my preferences table.
I can't figure it out. I've executed add_column before with no problems.
Has anyone ever seen this behavior before?
I see no reason for rails to fail the add of the column. You are probably looking in the wrong database.
The best way to debug this is to enter rails console mode:
script/console development
And create a new preference object and give mute_audio a value:
>> p = Preference.new
(...)
>> p.mute_audio = true
After the first command, you should see some output containing info about the newly created object. You should see that it has mute_autio: false. If setting the attribute mute_audio does not output an error, there's no problem and the new added column is there.
Related
I’ve started learning Ruby on Rails recently. I did a blogger tutorial. I want to add one more field archive to my database, but not sure is it possible just to write manually and which command to call?
Here is my database where I want to add code:
class CreateArticles < ActiveRecord::Migration[6.1]
def change
create_table :articles do |t|
t.string :title
t.text :body
t.timestamps
end
end
end
And I want to add a new field archive which is boolean and by default false?
Also is it okay to add in this schema new field or is it better in some other?
1.Run the migration from command line to add the new column
$ rails g migration add_column_archive_to_articles archive:boolean
The above command will create a new migration file in your db/migrate folder.
2.As of now there's no option to add default value to a column, which can be defined through terminal. Set the new column value to true/false by editing the new migration file created. Your migration file will look like this.
class AddColumnArchiveToArticles < ActiveRecord::Migration
def change
add_column :articles, :archive, :boolean, default: false
end
end
3.Then do
$ rails db:migrate
Migrations should never be changed afterwords. That is why they are migrations, and not a static schema definitions.
Just generate a new migration using rails g migration AddArchiveToArticles and then check the rails documentation for add_column to see how you can alter a table to add a column. It also supports default values :)
Let's say I want to add a column on my users table running the following migration
class AddVersionHistoryToUsers < ActiveRecord::Migration
def change
add_column :users, :versions, :string, array: true, default: '{}'
User.find_each do |user|
if user.app_version?
user.versions << user.app_version.to_s
user.save!
end
end
end
end
My aim is to insert the current app_version of each user into the versions array. How can I execute a migration without a lock in the users table due to the default value?
To wrap this up: Looping and querying the database for every User is highly inefficient. You should use update_all for these tasks.
And since your data already exists in the same table you can simply get it from there.
User.where.not(app_version: nil).update_all('versions = ARRAY[app_version]')
I have a migration which adds a foreign key to the users table.
[timestamp]_add_group_id_to_users.rb
Inside this migration file, I'm specifying a default value for it:
def self.up
add_column :users, :group_id, :integer, :default => 1
end
But what happens after a while when let's say a user has now group_id: 4
and another feature will be pushed to production?
Will all the migration files be run again?
Will group_ids of users be overwritten?
Should I specify something like "overwrite it if group_id is nil but don't do it if it's not and if it's different from 1" ?
Migrations are additive, means only new migrations will be executed.
In my app I have teams and each team has a game time every week. I want the game times to be set to 'now' as a default. My table is set up like so
create_table "teams", force: true do |t|
t.datetime "wk1_time"
end
I created a migration and it looks like this:
class ChangeDateTimeDefault < ActiveRecord::Migration
def change
change_column :teams, :wk1_time, :default => DateTime.now
end
end
When I run rake db:migrate I get an error. Is my syntax wrong or am I missing something else?
Since Rails 5 you can make a migration like this:
change_column_default :users, :wk1_time, -> { 'CURRENT_TIMESTAMP' }
In my mind this is the best option because it not database specific answer.
Yes, you are missing the type :
class ChangeDateTimeDefault < ActiveRecord::Migration
def change
change_column :teams, :wk1_time, :datetime, :default => DateTime.now
end
end
But, you need the below not the above one, because you just want to change the default.
class ChangeDateTimeDefault < ActiveRecord::Migration
def change
change_column_default :teams, :wk1_time, DateTime.now
end
end
But none of these are correct approach for your task. The reason is DateTime.now will be evaluated based upon when you ran the migration, instead when the record is created. You need look to into this answer to know how to set the default time.
EDIT: For Rails 5+ there are better answers, like this one: https://stackoverflow.com/a/55357711/252799, though the below still works.
The way I found, was to do a migration on an existing datetime column, like this:
#migration
execute("ALTER TABLE teams ALTER COLUMN wk1_time SET DEFAULT CURRENT_TIMESTAMP")
that produces a schema.rb entry shown like this:
#schema.rb
t.datetime "wk1_time", default: "now()", null: false
The "now()" is a string sent to postgresql and evaluated at runtime, upon each insert.
You're going to run into problems settings the default date time in the migration. This is because DateTime.now will be evaluated based upon when the migrate runs, not when the record is created!
To fix that you'll need to create an ActiveRecord callback in order to set wk1_time like so:
before_create :set_default_wk1_datetime
def set_default_wk1_datetime
self.wk1_time = DateTime.now
end
for Postgresql :
add_column :users, :msgs_seen_at, 'TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP'
but you'll have to use user.reload after user = User.create in order to "see" msgs_seen_at
I run:
rails generate migration AddHidemsgColumnToPublishers hide_msg:boolean
and the next file was created:
class AddHidemsgColumnToPublishers < ActiveRecord::Migration
def change
add_column :publishers, :hide_msg, :boolean
end
end
I want to set a default value into hide_msg by false.
so I tried:
rails generate migration add_default_value_to_hide_msg
class AddDefaultValueToHideMsg < ActiveRecord::Migration
def up
change_column :publishers, :hide_msg, :boolean, :default => false
end
def down
change_column :publishers, :hide_msg, :boolean, :default => nil
end
end
but I got errors:
rake db:migrate
== AddHidemsgColumnToPublishers: migrating ===================================
-- add_column(:publishers, :hide_msg, :boolean, {:default=>false})
rake aborted!
An error has occurred, this and all later migrations canceled:
PG::Error: ERROR: column "hide_msg" of relation "publishers" already exists
: ALTER TABLE "publishers" ADD COLUMN "hide_msg" boolean DEFAULT 'f'
Just a suggestion.... do not set the default value in the database, as you might face some problems if you want to change it latter on.
I think it is better if you set it in your model.
before_save :set_default_high_msg
def set_default_high_msg
self.high_msg ||= false
end
or even in your controller:
def new
#publisher = Publisher.new
#publisher.high_msg ||= false
...
end
rake db:migrate
== AddHidemsgColumnToPublishers: migrating ===================================
-- add_column(:publishers, :hide_msg, :boolean, {:default=>false})
Above thing means its trying to create colimn which already exist. If you check the class name AddHidemsgColumnToPublishers . This means it trying to execute your first migration in which you don't have default value. This clearly indicates you are doing something wrong.
publishers already has column hide_msg. So check the table description in db console. If you don't have any values in hide_msg then you can manually drop the column and re-run the rake db:migrate. Or you can keep that column as it is and just create migration to add default value.
change_column_default :publishers, :hide_msg, false
Doc: http://apidock.com/rails/ActiveRecord/ConnectionAdapters/SchemaStatements/change_column_default