ActiveRecord change column default value (not working) - ruby-on-rails

I've got a companies table in my database with two columns (size and state) that I wish to add default values to. Currently there is no default value and the columns are not set to null: false. I've tried using the update_column and update_column_default methods as outlined in the docs, but neither seems to be doing anything. I'm able to run the migration but there are no changes to the table.
I've tried update_column like so:
class AddDefaultValuesToCompanies < ActiveRecord::Migration
def change
def up
change_column :companies, :state, :string, default: 'MA'
change_column :companies, :size, :string, default: '1-10'
end
def down
change_column :companies, :state, :string, default: nil
change_column :companies, :size, :string, default: nil
end
end
end
I've also tried using update_column_default like so:
class AddDefaultValuesToCompanies < ActiveRecord::Migration
def change
def up
change_column_default(:companies, :state, 'MA')
change_column_default(:companies, :size, '1-10')
end
def down
change_column_default(:companies, :state, nil)
change_column_default(:companies, :size, nil)
end
end
end
What am I missing here?

Your migration will only change the default value for newly created records. I would write a rake task to update the existing values in the DB. Use something like
Companies.where(state: "").map{|c| c.update_attribute(state: "ma")

Related

Rails Activerecord adding columns to multiple tables

I need to make a migration where a column is added to three separate tables. The views column is an integer and needs to also default => 0. How can I add these columns with activerecord commands? I am using postgreSQL database.
Here is how my migration should look like:
class AddViewsToRestaurantsMenusDishes < ActiveRecord::Migration[6.0]
def change
add_column Restaurant, :views, :integer, :default => 0
add_column Menu, :views, :integer, :default => 0
add_column Dish, :views, :integer, :default => 0
end
end
add_column(table_name, column_name, type, **options)
class AddViewsToRestaurantsMenusDishes < ActiveRecord::Migration[6.0]
def change
add_column :restaurants, :views, :integer, default: 0
add_column :menus, :views, :integer, default: 0
add_column :dishes, :views, :integer, default: 0
end
end
None of the methods in ActiveRecord::ConnectionAdapters::SchemaStatements or ActiveRecord::ConnectionAdapters::TableDefinition which make up the whole migrations DSL take models as arguments - its all tables all the way.
This assumes that your tables are conventionally named. If you really wanted to do the same thing from a list of model classes you could do:
class AddViewsToRestaurantsMenusDishes < ActiveRecord::Migration[6.0]
def change
[Restaurant, Menu, Dish].each do |model|
add_column model.table_name, :views, :integer, default: 0
end
end
end
But for migrations its usually a good idea to KISS. Especially since they are not supposed to stick around for the entire lifetime of your project.

Remove boolean and add list in ruby on rails

I would like to modify gender field,
Initially i have declared gender field as boolean true or false. but now i want it to be changed as list (Male, Female, Other).
class AddExtraFieldsToUser < ActiveRecord::Migration
def change
add_column :users, :phone_number, :string
add_column :users, :date_of_birth, :datetime
add_column :users, :gender, :boolean, default: false
add_column :users, :live_in, :string
add_column :users, :description, :text
end
end
Can i modify as following.... please let me know the correct way...
i thought of doing rails g migration RemovegenderFromUsers gender:boolean
then rake db:migrate followed by creating new one
rails g migration AddGenderToUsers gender:select
user.rb
GENDER_TYPES = ["Male", "Female", "Other"]
html
<%= f.select :gender, User::GENDER_TYPES %>
Is above mentioned process correct or any other way ?
The answer by Ahmad Hussain is correct . List is not a database field type .
You should generate a migration to change the column type :
**change_column :table_name, :column_name, :type**
Select is not a database field type if you want to do it then do it like this
rails g migration AddGenderToUsers gender:integer
In migration file change it to like this:
change_column :users, :gender, :integer, default: 0
For form page do this:
<%= f.select :gender, User::GENDER_TYPES.each_with_index.map { |gender, index| [gender, index] } %>
And in user model you can define function to get gender name to display
def gender_name
GENDER_TYPES[gender]
end

Updating Attribute Value in Rails Migration

I have over 100 recipes uploaded through ActiveAdmin (http://activeadmin.info/) in with the following attributes:
class CreateRecipes < ActiveRecord::Migration
def change
create_table :recipes do |t|
t.string :title
t.string :description
t.string :ingredients
t.string :position
t.timestamps
end
end
end
I needed to change position from a string to an integer. I was able to do this with the following:
change_column :table_name, :column_name, :integer
stackoverflow: Rails migration for change column
The issue is that I do not know how to go back and reassign all the recipes with a position (now that it's an integer). I basically want to start at 0 and go all the way to 100. And if i create a new recipe, it automatically has the position value of 101.
Is there a way to do this without going back and individually changing each recipe?
It sounds like you want to set :position to :id initially. You could do that through the rails console like so:
recipes = CreateRecipes.all
recipes.each do |recipe|
recipe.position = recipe.id
end
Then, for new recipes, in your model (create_recipes.rb), you could add:
after_initialize :default_values
...
def default_values
self.position ||= id
end
Incidentally, this is a nice clean way to handle default or initial values in general. For more information, see this excellent post How can I set default values in ActiveRecord?.
You can have the conversion run automatically as part of the migration itself. Add the code to convert the values in the existing records into the migration. Use self.up and self.down to have the appropriate conversion code for that direction of the migration:
class ChangeRecipePositionToInteger < ActiveRecord::Migration
def self.up
position_values = Hash[ Recipe.all.map{|r| [r.id, r.position]}]
change_column :recipes, :position, :integer
position_values.each_pair do |id, position_value|
recipe = Recipe.find( id )
recipe.position = position_value.to_i
recipe.save
end
end
def self.down
position_values = Hash[ Recipe.all.map{|r| [r.id, r.position]}]
change_column :recipes, :position, :string
position_values.each_pari do |id, position_value|
recipe = Recipe.find( id )
recipe.position = position_value.to_s
recipe.save
end
end
end

Rails: change column type, but keep data

I have a model with a column of type integer which I want to convert to type string. Now I'm looking for the best way to change the column type without losing the data. Is there a painless way to accomplish this?
A standard migration using the change_column method will convert integers to strings without any data loss. rake db:rollback will also do the reverse migration without error if required.
Here is the test migration I used to confirm this behaviour:
class ChangeAgeToString < ActiveRecord::Migration
def self.up
change_column :users, :age, :string
end
def self.down
change_column :users, :age, :integer
end
end
for postgres
in migration
change_column :table_name, :field,'boolean USING (CASE field WHEN \'your any string as true\' THEN \'t\'::boolean ELSE \'f\'::boolean END)'
and to any valid type similar
for postgresql, change table column datatype integer to string,
rails migration like this with up and down actions
class ChangeAgeToString < ActiveRecord::Migration
def self.up
change_column :users, :age, 'varchar USING CAST(age AS varchar)', null: false
end
def self.down
change_column :users, :age, 'integer USING CAST(age AS integer)', null: false, default: 0
end
end
If it's a one-off you can just change the column type in the database (since no info is lost moving from int to varchar)
For MySQL, this would do it:
ALTER TABLE t1 MODIFY col1 VARCHAR(256)
If you're using SQLite you won't have to do anything.
You can try something like this:
change_column :table_name, :column_name, 'integer USING CAST(column_name AS integer)'
or even better:
change_column :table_name, :column_name, :integer, using: 'column_name::integer'
You can read more about this topic here: https://kolosek.com/rails-change-database-column
If you use Postgres, you can't implicitly cast a string back to an integer, so the way to make the change reversible is:
class ChangeAgeToString < ActiveRecord::Migration
def self.up
change_column :users, :age, :string
end
def self.down
add_column :age_integer
User.connection.execute('UPDATE users SET age_integer = cast(age as int)')
remove_column :users, :age
rename_column :users, :age_integer, :age
end
end

Migrations: change column from integer to string

Can anyone show me how to edit the following migration to change :phone integer to string?
class CreateContactInfos < ActiveRecord::Migration
def change
create_table :contact_infos do |t|
t.integer :phone
t.string :facebook
t.references :user
t.timestamps
end
add_index :contact_infos, :user_id
end
end
Thanks in advance!
I guess you already migrated the one you're showing, so create another in which you'd put:
change_column :contact_infos, :phone, :string
I have added some more explanation to this.We need to generate a new migration
rails g migration change_phone_to_be_string_in_contact_infos
If we open up the migration we should see something like this
class ChangePhoneToBeStringInContactInfos < ActiveRecord::Migration[5.0]
def change
end
end
What we call this migration will have no impact on what we need to do next, but future we and other developers will thank us for naming our migration appropriately.
As you can see the change method is sitting empty. We need to manually add some code here.
class ChangePhoneToBeStringInContactInfos < ActiveRecord::Migration[5.0]
def change
change_column :customers, :phone, :string
end
end
After Saving this file just do rake db:migrate we can see changes we want.
For a reversible migration, use:
def up
change_column :contact_infos, :phone, :string
end
def down
change_column :contact_infos, :phone, :integer, using: "phone::integer"
end
Convert column type string into integer in rails migration :
def change
change_column :contact_infos, :phone, :integer, using: 'phone::integer'
end
Convert column type integer into string in rails migration:
def change
change_column :contact_infos, :phone, :string, using: 'phone::string'
end

Resources