Rails : Moving a column from a table to another table - ruby-on-rails

I'm a beginner and I can't move a table column(doctors.specialty) to another table column(specialties.name).
This is the doctors table:
tp Doctor.all
ID
FIRST_NAME
LAST_NAME
SPECIALTY
ZIP_CODE
CREATED_AT
UPDATED_AT
CITY_ID
1
Cathy
Wyman
Gynecologist
78328
2022-07-28 13:48:57
2022-07-28 14:33:51
17
2
Dave
Howell
General practitioner
97544
2022-07-28 13:48:57
2022-07-28 14:33:51
13
3
Zachariah
Stamm
Anesthesiology
08435-1702
2022-07-28 13:48:57
2022-07-28 14:33:51
4
4
Forrest
Koelpin
General practitioner
58486
2022-07-28 13:48:57
2022-07-28 14:33:51
16
5
Cameron
Zieme
Anesthesiology
01200
2022-07-28 13:48:57
2022-07-28 14:33:51
13
6
Chia
Borer
Gynecologist
81913-8875
2022-07-28 13:48:57
2022-07-28 14:25:11
18
7
Sonny
Torphy
Gynecologist
08593-7841
2022-07-28 13:48:57
2022-07-28 14:33:51
4
I generated a new model:
rails g model Specialty name:string doctor_id:integer
I generated a new migration
rails g migration MoveColumnDataToSpecialty
I tried this code but it didn't work and i'm not sure that I should use it to move the column
class MoveColumnDataToSpecialty < ActiveRecord::Migration[5.2]
def self.up
add_column :users, :some_property, :string
execute "UPDATE users u SET some_property = p.some_property FROM profiles p WHERE u.id = p.user_id;"
remove_column :profiles, :some_property
end
def self.down
add_column :profiles, :some_property, :string
execute "UPDATE profiles p SET some_property = u.some_property FROM users u WHERE p.user_id = u.id;"
remove_column :users, :some_property
end
end
Thank you in advance

Firstly you need to create specialties table, then add column with FK to doctors table. After that insert into specialties existed names and make associations between tables. Finally you can remove unused column
Rollback migration -- reverse order
def self.up
create_table :specialties do |t|
t.string :name
end
change_table :doctors do |t|
t.belongs_to :specialty, foreign_key: true
end
specialties = Doctor.pluck(:specialty).uniq
specialties_ids_names =
Specialty.insert_all!(
specialties.map { |specialty| { name: specialty } },
returning: %w[id name]
)
specialties_ids_names.rows.each do |specialty_id, name|
# Here raw SQL column to prevent disambiguates with association
Doctor.where('doctors.specialty = ?', name).update_all(specialty_id: specialty_id)
end
remove_column :doctors, :specialty
end
def self.down
add_column :doctors, :specialty, :string
Specialty.pluck(:id, :name).each do |specialty_id, name|
# Here raw SQL column to prevent disambiguates with association
Doctor.where(specialty_id: specialty_id).update_all("specialty = #{name}")
end
remove_column :doctors, :specialty_id
drop_table :specialties
end
Note: insert_all was introduced in Rails 6. Instead of this method, you can create records one by one or use raw SQL INSERT INTO specialties VALUES using ActiveRecord::Base.connection.insert_sql

Related

Rails migration with add_foreign_key: 'column "user_id" referenced in foreign key constraint does not exist'

(Rails is version 5.0.0, Ruby 2.3.0p0)
I want to create an association between my Users table and Cards table. I've added belongs_to :user to the Cards model, and has_many :cards to the Users model, and created a migration with:
class AddUserIdToCard < ActiveRecord::Migration[5.0]
def change
add_foreign_key :cards, :users, column: :user_id
end
end
When I run rake db:migrate, I receive the error:
ActiveRecord::StatementInvalid: PG::UndefinedColumn: ERROR: column "user_id" referenced in foreign key constraint does not exist
: ALTER TABLE "cards" ADD CONSTRAINT "fk_rails_8ef7749967"
FOREIGN KEY ("user_id")
REFERENCES "users" ("id")
Now I initially worked around this problem simply by adding add_column :cards, :user_id, :integer to the migration, but that doesn't really seem very tidy, and I'm worried about problems coming up later. Is there a better way to accomplish this?
You're setting a foreign key for cards table with the column user_id. But you haven't created a reference yet. Create a reference and then add foreign key to maintain referential integrity. Rollback and modify your migration with
1 class AddUserIdToCard < ActiveRecord::Migration[5.0]
2 def change
3 add_reference :cards, :users, index:true
4 add_foreign_key :cards, :users
5 end
6 end
Line 3 will create, in the cards table, a reference to id in the users table (by creating a user_id column in cards).
Line 4 will add a foreign key constraint to user_id at the database level.
For more, read Add a reference column migration in Rails 4
The answer provided is not accurate for Rails 5. Scroll to the add_reference bit of the docs for more, but in the case of the above question, you would use:
class AddUserIdToCard < ActiveRecord::Migration[5.0]
def change
add_reference :cards, :users, foreign_key: true
end
end
In rails 6, I believe this is now
def change
add_column :cards, :user_id, :integer, index: true
add_foreign_key :cards, :users
end
class AddUserIdToCard < ActiveRecord::Migration[5.2]
def change
add_foreign_key :cards, :users, column: :user_id, primary_key: :"id", on_delete: :cascade
end
end
Try this migration. I was facing the same problem then I fixed it.

Rails 4: How do I add a column to ALL models in the application?

My Ruby_2+Rails_4+Postgres_9.2 app contains tens of models. A recent CR requires me to add two new fields to ALL of my models. I know I can painstakingly create a migration which will individually 'add_column' to each of my models. Is there any easier way of doing the same where I wouldn't need to do that?
def self.up
ActiveRecord::Base.connection.tables.each do |table|
next if table == "schema_migrations"
add_column table, :column_name, :column_type
end
end
EDIT:(getting table name from models)
def self.up
Rails.application.eager_load!
ActiveRecord::Base.descendants.each do |model|
add_column model.table_name, :column_name, :column_type
end
end
def tables
(ActiveRecord::Base.connection.execute
"SELECT table_name FROM INFORMATION_SCHEMA.TABLES
WHERE table_schema = '#{ActiveRecord::Base.connection.current_database}'
AND table_type = 'BASE TABLE'").to_a.flatten
end
def self.up
tables.each do |table|
add_column table, :a_column1, :a_type
add_column table, :a_column2, :a_type
end
end
Add a migration and loop through all the tables:
def self.up
[:table_name1, :table_name2, :table_name3, ...].each do |table_name|
add_column table_name, :column1, :string
add_column table_name, :column2, :string
end
end

Learning Rails 3.0 - Migration Help - belongsTo

I'm working on a photo gallery app. Photo has a belongsTo relationship to Album (Album has_many realtionship to Photo) How do I create the migration that adds this relationship to the database correctly? I have tried - rails generate add_album_to_photo but that comes through as an empty migration. I could use a push in the right direction.
Assuming tables albums and photos exist already, all you have to do is add a column album_id to your photos table:
class AddAlbumToPhoto < ActiveRecord::Migration
def self.up
add_column :photos, :album_id, :integer
end
def self.down
remove_column :photos, :album_id
end
end
Or:
class AddAlbumToPhoto < ActiveRecord::Migration
def self.up
change_table :photos do |t|
t.references :album
end
end
def self.down
change_table :photos do |t|
t.remove :album_id
end
end
end
Or if you insist on generating the code:
rails g migration add_album_to_photo album_id:integer

Rails migration to reference a User, how to add this foreign key?

I have a 2 Models, User and Post.
I want the Post model to have a User in it i.e. add a user_id in the Posts table.
What will my migration look like? Or do I just do this manually like:
add_column :posts, :user_id:integer
yep, just do it manually. It'll look like this:
class AddUserIDToPosts < ActiveRecord::Migration
def self.up
add_column :posts, :user_id, :integer
end
def self.down
remove_column :posts, :user_id
end
end

How can I add a column to reference to another table on RoR?

Here is the Customer:
class CreateCustomer < ActiveRecord::Migration
def self.up
create_table :customers do |t|
t.column :email, :string, :null => false
end
end
def self.down
drop_table :customers
end
end
And this is the customer Info:
class CustomerInfo < ActiveRecord::Migration
def self.up
create_table :statuses do |t|
t.column :statuses, :string, :null => false
end
end
def self.down
drop_table :status
end
end
What I would like to do is the customer and customer Info have a one to one relationship. How can I do it in a new migration? thank you.
When you want a 1 to 1 in Rails, you have to decide which one of the models will store the foreign key. In your case, you probably want status to store the fk, so add an integer column called customer_id to the status table. Then you can add the has_one/belongs_to on Customer and Status. belongs_to always goes on the model with the foreign key.
Also I'm not sure if Rails will like you calling your table with the singular name, so you will probably have to do some extra work if you really want to call it 'status' instead of 'statuses'
You can try following thing in your next migration
add_column :customer_infos , :customer_id , :integer ,:references=>"customers" , :null=>:true
Then you can add the has_one/belongs_to on Customer and Cusomer_infos .
You can also execute an SQL statement.
statement = "ALTER TABLE users CHANGE id id SMALLINT( 5 ) UNSIGNED NOT NULL AUTO_INCREMENT" ActiveRecord::Base.connection.execute(statement)
you can entry manually in your migration
Note this is just an example. The final SQL statement syntax depends on the database.

Resources