Add a foreign key to an existing rails model - ruby-on-rails

I have a rails 4 app.
I have two tables, one for 'scope' and one for 'data'. Data belongs to scope. I forgot to add a foreign key when I set up data and I'm trying to write a migration to add one now.
I have created a change table, but the migration I've written isn't working.
I can't follow the rails guides example because it isn't consistent with the experience I'm having in my setup (not sure why).
The migration I have is:
class AddFKeyToData < ActiveRecord::Migration
def change
add_foreign_key :data, :scopes
end
end
Please can you help me identify the problem.
Thank you

Rollback this migration by:
rake db:rollback
Then go into your migration and edit add_foreign....
to:
add_column :data, :scope_id, :integer
Should work!

Related

How to add a new column to every model?

with what migration can I add a column to every model at once? This would be handy almost every time you forgot to add a field anyway because most of the time a field you want to add is not limited to one model only. Like "email" In this case I forgot to add an "order" field.
Thanks
You can do as follows -
def change
tables = [:users, :products, :addresses]
tables.each do |table_name|
add_column table_name, :order, :integer
end
end
It's called a group migration
Answering your first question "With what migration can I add a column to every model at once?". Answer: None. Rails migrations are a way to alter database schemas over time in a consistent way.
Rails migrations are Ruby classes using Rails methods as instructions to modify your database as needed. So your question could be better formulated as "How can I create a migration to add a column to every model at once?"
IMHO I don't think there's going to be a specific method to do this, as the requeriment is pretty custom, but, depending in your Rails version you can get all the ApplicationRecord.descendants:
Zeitwerk::Loader.eager_load_all
ApplicationRecord.descendants.map { |table| table.name.downcase.pluralize }.each do |table|
add_column table, :logdate, :datetime
end
Or all those tables from the database that can be safe_constantized:
ActiveRecord::Base.connection.tables.map do |table|
table.classify.safe_constantize
end.reject(&:nil?).each do |table|
add_column table, :logdate, :datetime
end
That way you get the name of each table/model and use it as the first argument for add_column.
The difference is that in Rails 6 the default code loader is Zeitwerk, so you can eager load all the project dependencies. In other versions you could do the same but using Rails.application.eager_load!.
The second version would work without having to load the models as dependencies as it makes a query asking for their tables and then maps them as constants.

Rails migration to remove field

Hello I am about to attempt to run a rails migration on a database of skills that has a :title and :description. I need to remove the description field and I assume it will look something like this:
rails migration remove_column :skills, :description
I am running it by you pros before I attempt it and end up breaking something on accident. Please let me know if I have the right idea about removing the description field from the database. Thanks a lot!
If skills is the name of your table and description is the name of the column you want to remove, you can type rails g migration RemoveDescriptionFromSkills in your terminal. This will generate a migration file with the name [timestamp]_remove_description_from_skills.rb, located in db/migrate. Edit this file so that it contains the following:
class RemoveDescriptionFromSkills < ActiveRecord::Migration
def change
remove_column :skills, :description
end
end
Then type rake db:migrate in your terminal, and the column will be removed.
For more information, check out this helpful Stack Overflow post: https://stackoverflow.com/a/1992045/3723769.
Note: this answer is intended to describe how to perform the migration. As a safety measure, you should do what Michael Durrant advises before migrating: https://stackoverflow.com/a/25006727/3723769.
Here's some of the things that some to mind:-
check the existing values to see if there's any data you want
see if any indexes exist that should also be dropped.
search the application for that field name
see if there's an existing rails migration that you can use to DOWN the change
Finally, I would consider creating a change migration as normal, i.e. one that actaully adds the field and then I would run it using the down syntax to remove the field.

Guidelines about using of migrate on Rails

I have already read the documentation of "migrate" on Rails 3 (Rails 3.0 Relese Notes Migrate) but I have some doubts.
e.g. I created two class:
rails generate scaffold User name:string age:integer height:float
rails generate scaffold Hat type:string width:float height:float
This create models, controllers, ... User and Hat and its migrate class: xxx_create_users.rb and xxx_create_hats.rb
Ok, now we guess we want modify User class and we delete height attribute, and we add the relationships between Users and Hats:
User
class User < ActiveRecord::Base
attr_accessible :name, :age
has_many :hats
end
Hat
class Hat < ActiveRecord::Base
attr_accessible :type, :width, height
belongs_to :user
end
Options that I guess:
I remove all files xxx_create_xxx.rb and then I will create again with: rails generate migration CreateUser (and the same for Hat)
I create a new migration file: rails generate migration MyNewMigration where I codify by hand all changes.
Is there another way to automate changes in my classes for passed it to the database? What is the correct way to proceed?
The idea of migrations is that you have a stringent storyline where you can start at any point, forward and backwards. This means that it should not ever be necessary to delete a migration.
Instead, you create a new migration that will change, remove or add database fields.
In your example, you would leave the old migration where it is and then create a new migration like so:
rails g migration change_user_fields
And inside def up you write
remove_column :table_name, :column_name
change_column :table_name, :column_name, :data_type
Add a def down - this will be run whenever the migration is reversed by rake db:rollback. Inside def down put:
add_column :table_name, :column_name # add the field that you removed (s.a.)
change_column :table_name, :column_name, :data_type # change back to old data type
Rails 3 gives you a nice shortcut for adding and removing fields from a table by doing:
rails g migration add_something_to_users name:string
which will automatically create a migration that adds a field called name with a data type of string to the users table. Or
rails g migration remove_something_from_users name
which will automatically create a migration to remove the name field from the users table. These shortcuts and the created migration files do not need a def down - rails will automatically be smart enough to figure that out when reversed.
In both cases, you can replace the word "something" with whatever you like.
However, I know of no way to use a shortcut to change data types, so you need to go in the migration file and do that manually.
Eventually, just run rake db:migrate and you'll be all set!
Scaffold only automate a fixed command, so if you create a model and its attributes with scaffold, your migration will only contain the fields you specify on the command line. There is no way to keep track of changes automatically.
If you add/remove/change something on your database, you have to manually set it. Migrations are useful because you can keep track of these changes on time.
So i recommend you to never delete a migration. In this particular case you described, you just have to create another one to reflect the new change on your database, keeping a total of 3 migrations, instead of deleting and creating another.
The answer to your question should be alternative 2. But you don't have to do it all by hand if you just want to add or remove attributes:
http://guides.rubyonrails.org/migrations.html#creating-a-standalone-migration

Adding a column through the terminal

How do you add a column to a table using ActiveRecord through the terminal. I am trying to use add_column method but its not working. Any ideas please?
It is better to write a migration and a must if you are working with a team. When you make db changes, then every developer's environment has to be updated also. Otherwise, you will have some mad developers at you.
rails generate migration AddPartNumberToProducts part_number:string
will generate
class AddPartNumberToProducts < ActiveRecord::Migration
def change
add_column :products, :part_number, :string
end
end
Then you run the migration
rake db:migrate
http://guides.rubyonrails.org/migrations.html
Edit:
For a rails console command line check #tadman's answer or use what Bengala proposed like
ActiveRecord::Migration.add_column :products, :part_number, :string
You can run migrations directly in rails console rails c with ActiveRecord::Migration
For your purpose the next command will do what you ask:
> ActiveRecord::Migration.add_column :table_name, :field_name, :field_type
If you're just hacking around, it's usually easier to manipulate the database using a SQLite client of some sorts than through the Rails DB layer.
If you're doing this for a project, create a proper migration file and run it.
If you're determined to do this, the add_column method is available through the ActiveRecord::Base.connection driver object.

Ruby on Rails database: how to mention foreign key relation by changing app/model/table.rb file how to know they implemented

I am new to RoR. I used "rails generate model ServiceConfigs" command to generate a table.
so may commands are as below
rails generate model ServiceConfigs configs:string
rake db:migrate
-- can see app/db/service_config.rb created.
Now edited service_config.rb file to add 2 columns and which has a foreign key relation to service table. So here is code
class ServiceConfigs < ActiveRecord::Base
belongs_to :service, :dependent => :destroy
validates_presence_of :configs
end
Now when I login to underline database, and look at the table I don't see foreign key relation in table schema. Whats wrong/more I need to do?
highly appreciate your time and help
Krishna
So a few notes for you as you're getting started here: Firstly, I'd name your model in the singular form, as though you are describing a single instance of it: ServiceConfig and in the case where it belongs to a service, you may want to think of a different name all together.
Secondly validates_presence_of suggests that you have that field, so firstly, I'd remove the validation until you know that your DB has that column and that the migration worked. You can make sure by looking at your migration file and confirming that the column is being created.
You can define the relationship the "rails way" in your migration too:
http://guides.rubyonrails.org/association_basics.html
create_table :service_configs do |t|
t.string :configs
t.references :service
t.timestamps
end
Update
If you want to rerun a particular migration run:
rake db:migrate:redo VERSION=20100421175455
You can take a look in your schema to see what migration you are currently at as well. And you can also take a look at the schema to see what your DB is expected to look like as well.

Resources