Remove boolean and add list in ruby on rails - 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

Related

Rails: rename globalize column

The rails globalize gem docs are great, but I can't find a solution, what I have to do, when I want to rename a column.
Last Year I did that, to add the translation fields.
def up
remove_column :news, :name
News.add_translation_fields! name: :string
end
def down
add_column :news, :name, :string, default: nil
remove_column :news_translations, :name
end
Now I want to rename the column "name" to "title", without loosing my data and translations. How do I have to write the migration file?
Alter the column on the news translations table directly:
def change
rename_column :news_translations, :name, :title
end

ActiveRecord change column default value (not working)

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")

How to merge columns in a rails table?

I have a rails table of users, with columns first_name and last_name etc.
How do i merge these two together? Or how do i create a new column called name and add data from these two columns?
Basically i need a column called name which is the concatenation of the first_name and last_name.
As Richard Brown has answered, you should create a migration if you want to save the concatenated string to the database.
rails g migration add_fullname_to_users fullname
then run an sql inside the generated migration to update all records
# mysql
User.update_all('fullname = CONCAT(first_name, " ", last_name)')
# postgre
User.update_all("fullname = (first_name || ' ' || last_name)")
But I'd suggest you just keep your current setup and just create a method called fullname in your model
# user.rb
def fullname
"{first_name} #{last_name}"
end
which is better since you have access to first_name and last_name
If you want to make sure you can rollback, you can do something like this in your migration file.
def up
add_column :your_table, :name, :string
YourClass.all.each do |person|
person.update_attributes! :name => person.first_name + " " + person.last_name
end
remove_column :your_table, :first_name
remove_column :your_table, :last_name
end
And your down method for the roll back:
def down
add_column :your_table, :first_name, :string
add_column :your_table, :last_name, :string
YourClass.all.each do |person|
person.update_attributes! :first_name => person.name.match(/\w+/)[0]
person.update_attributes! :last_name => person.name.match(/\w+/)[1]
end
remove_column :your_table, :name
end
Create a migration to add the new field:
rails g migration AddNameToTableName name
You can write a rake task to do the join.
TableName.all.each do { |row| row.update_attributes(name: "#{row.first_name} #{row.last_name}") }
You can execute that from the rails console, but I like repeatable database updates so I prefer a rake task.
The \w regex matcher and concatenating with spaces as in the above examples were problematic for me, although I had the opposite problem, splitting a name into first and last names. Instead, consider using what I ended up with. Back up your databases first, and test this in development!
def up # Split name into first and last
add_column :people, :first_name, :string
add_column :people, :last_name, :string
Person.all.each do |person|
person.update_column(:first_name, person.attributes["name"].rpartition(" ").first)
person.update_column(:last_name, person.attributes["name"].rpartition(" ").last)
person.save(:validate => false)
end
remove_column :people, :name
end
def down # Merge first and last into name
add_column :people, :name, :string
Person.all.each do |person|
person.update_column(:name, [person.attributes["first_name"].to_s, person.attributes["last_name"].to_s].reject(&:blank?).join(" "))
person.save(:validate => false)
end
remove_column :people, :first_name
remove_column :people, :last_name
end
This avoids inserting stray spaces in case one of the names is nil, and also splits a fullname more effectively into first and last names; specifically, by splitting on the last space in the name, since "First Middle" "Last" is more of a human-compatible split than "First" "Middle Last". See examples:
# Before split
> puts p.name.inspect
"Prince"
"Test User"
"Jean Luc Picard"
# After split
> puts p.first_name.inspect + " " + p.last_name.inspect
"" "Prince"
"Test" "User"
"Jean Luc" "Picard"
My main complaint is that "Prince" should probably be a first name not a last name, but then again depending on your database constraints it might need to go into the last_name field anyway.
In my rails 4 app with a User model I generated my migration file
rails g migration RemoveFirstLastNameColumnsFromUser
Then I put in
class RemoveFirstLastNameColumnsFromUser < ActiveRecord::Migration
def up
add_column :users, :name, :string
User.all.each do |u|
u.update! name: "#{u.first_name} #{u.last_name}"
end
remove_column :users, :first_name
remove_column :users, :last_name
end
def down
add_column :users, :first_name, :string
add_column :users, :last_name, :string
User.all.each do |u|
n = u.name
u.update! last_name: n.slice!(/\w+\z/)
u.update! first_name: n.strip
end
remove_column :users, :name, :string
end
end
This #down method will account for names with more than one space.

Why am I getting an error when migrating? Uninitialized constant AddDates

I want to add a new column for a bunch of different items on my site.
class AddCreatedatToStreamItems < ActiveRecord::Migration
def change
add_column :stream_artworks, :createdat, :date
add_column :stream_experiments, :createdat, :date
add_column :stream_photographies, :createdat, :date
add_column :stream_webs, :createdat, :date
add_column :stream_socials, :createdat, :date
end
end
Any chance the filename of the migration is "123123_add_dates.rb" instead of "123123_add_createdat_to_stream_items.rb" (where '123123' is a string of digits, not necessarily '123123')?
The classname of the migration and the filename need to match in the CamelCase => camel_case way.

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