Rails removing a column from activerecord not working - sql

I want to remove container:references from my table, I have tried:
rails generate migration RemoveContainerfromCreateTasks container:references
followed by rails db:migrate, but it my reference field is still not removed.
Below is my ActiveRecord
class CreateTasks < ActiveRecord::Migration[6.1]
def change
create_table :tasks do |t|
t.string :title
t.text :body
t.references :container, null: false, foreign_key: true
t.text :tag
t.datetime :due
t.integer :priority
t.timestamps
end
end
end
class RemoveContainerfromCreateTasks < ActiveRecod::Migration[6.1]
def change
end
end

The issue here is really a sneaky capitalization error. Running:
rails generate migration RemoveContainerfromCreateTasks container:references
Will generate a migration with an empty change block which will do absolutely nothing when you migrate it except modify the migrations meta table (a table that AR uses to keep track of which migrations have been run). But if you properly capitalize From:
rails generate migration RemoveContainerFromCreateTasks container:references
It will generate:
class RemoveContainerFromCreateTasks < ActiveRecord::Migration[6.0]
def change
remove_reference :create_tasks, :container, null: false, foreign_key: true
end
end
Rails isn't actually intelligent. It just casts the name argument into snake case and compares it to a set of patterns like:
remove_something_from_tablename foo:string bar:integer
create_tablename foo:string bar:integer
create_foo_bar_join_table foo bar
And it then uses a template to generate the according type of migration. If you don't properly pluralize it will be cast into:
remove_containerfrom_create_tasks
Which Rails does not know what to do with as it does not match a known pattern.
Also note despite popular belief migrations are just a DSL to create SQL transformations which is completely unaware about your tables or models. In this case the resulting migration will just blow up when you attempt to run it since you don't have a create_tasks table.
I would roll the missnamed migration back. Delete it then run:
rails g migration RemoveContainerFromTasks container:references
rails db:migrate

Your issue here is that "CreateTasks" is not table in your database. "Tasks" is, however.
rails g migration RemoveContainerFromTasks container:references
will provide you
class RemoveContainerFromTasks < ActiveRecord::Migration[6.1]
def change
remove_reference :tasks, :container, null: false, foreign_key: true
end
end
A migration of this will successfully remove container from your schema.rb file, and subsequently the database system you're using.
Here's some console output, because why not:
unclecid#home:~/Desktop/sample_app$ rails db:migrate
== 20201227150512 CreateTasks: migrating ======================================
-- create_table(:tasks)
-> 0.0022s
== 20201227150512 CreateTasks: migrated (0.0023s) =============================
== 20201227151021 RemoveContainerFromTasks: migrating =========================
-- remove_reference(:tasks, :container, {:null=>false, :foreign_key=>true})
-> 0.0330s
== 20201227151021 RemoveContainerFromTasks: migrated (0.0331s) ================

Related

Will renaming Rails migration files affect my live app?

I edited to contents of some of my migration files to solve an issue between Paperclip and an Attachment model, renaming it to Upload.
01234_create_attachments.rb
class CreateAttachments < ActiveRecord::Migration
def change
create_table :attachments do |t|
t.string :name
t.string :attachment_url
t.timestamps
end
end
end
became this:
01234_create_attachments.rb
class CreateUploads < ActiveRecord::Migration
def change
create_table :uploads do |t|
t.string :name
t.string :upload_url
t.timestamps
end
end
end
Note that I only edited the file contents, not the file name.
The existing app runs fine but now I can't git clone the repo to a new server because rake db:migrate fails. If I then edit the actual migration filenames on the new server it runs properly:
01234_create_attachments.rb > 01234_create_uploads.rb
My question is if I rename the migration files in my master branch will it cause problems with my existing live app when I rake db:migrate in the future?
Your file name and class name of migration should match.
schema_migrations table is used to determine if the migration should run or not. Since that table only has timestamp, unless you change the timestamp, it wouldn't rerun the migration.
Its not advised to change existing migration after it has been run on production environment. Add a new migration if you have to change anything.

How do i make rails 3 model type cast according to migration?

The problem that i have is when i run my migrations the updates is applied to the database, but rails does not do the same.
To be more accurate. I have a address model with a house number. Recetly i was told that the house number should be able to contain letters, like (35B). Therefore i would like to convert the integer colum to a column of strings. This is no problem in any case with my data, red. only integers.
The migration that i applied works as expected. It changes the type of the column in the postgres database and preserves the data content. I was using this migration.
class ConvertIntToStringOnNumber < ActiveRecord::Migration
def change
change_table :addresses do |t|
t.change :number, :string
end
end
end
Migration result with this schema.rb
create_table "addresses", :force => true do |t|
t.string "street"
t.string "number"
t.integer "zip"
t.string "floor"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
After running the migration on my heroku server i am not able to query the database using data from the form, this was no problem before. Rails is trying to look for an integer, but the database is containing strings.
Rails is trying to run this query, even though the schema.rb says something different. This is where the party stops.
SELECT "addresses".* FROM "addresses" WHERE "addresses"."street" = 'xxxx' AND "addresses"."number" = 63 AND "addresses"."floor" = '' AND "addresses"."zip" = 9000 LIMIT 1):
I have seen a lot of problems with forigen keys, but this is NOT one of those problems.
Did you restart your application after running the migration?
ActiveRecord loads information about your tables into each class when they are instantiated. See #columns for more info. Since Heroku runs your app in production mode, your classes won't be automatically reloaded on each request.
Try running heroku restart on your application - Rails should pick up the changes then.

Getting paranoia deleted objects through polymorphic relation in Rails 3

I have an Audit class which is used to store action, by and on attributes.
class Audit < ActiveRecord::Base
attr_accessible :activity
belongs_to :by, :polymorphic => true
belongs_to :on, :polymorphic => true
attr_accessible :by, :on
end
The polymorphic association for :by and :on is used to store any kind of objects which should be audited. Mainly because polymorphic is broken down into type and id in the schema and thus should be able to store all my model objects.
create_table "audits", :force => true do |t|
t.string "activity"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.integer "on_id"
t.string "on_type"
t.integer "by_id"
t.string "by_type"
end
The problem I'm having comes from that all of the objects being audited are also using the paranoia gem. The paranoia gem introduces a deleted_at column in each models table which is checked for every query on that model through its default_scope which is set to something like "where(deleted_at is null)". The paranoia gem also provides a method .with_deleted which allows a direct query by turning of the default_scope and as a result also return objects which have beend paranoid / soft deleted.
However if I have any items that are deleted and I try to get all my Audited items listed using.
Audit.all
I can't figure out how to tell Rails to run the lookup query for each polymorphic :by and :on object adding the .with_deleted call. My guess is that rails looks up the the object of a polymorphic relation by
eval(type).find(id)
Which in my case would give me the object with the default_scope of the paranoia gem applied.
I have tried to override self.find_by_sql in Audit but with no luck. I get stuck in some Arel methods which I need to read up on more before I can move on.
I see the following solutions but I can't figure out how to do them.
Overwrite the polymorphic lookup. How?
Get the raw SQL as string before it's evaluated and sub/gsub the Where deleted_at is null part.
Any and all suggestions on how to approach this would be greatly appreciated.
Try this:
def on!
if on_type && on_id
on_type.constantize.with_deleted.find(on_id)
end
end
This will raise an ActiveRecord::RecordNotFound error if the record has been truly deleted, but otherwise will return the "on" object even if it is marked as deleted.

Rails 3 HABTM join table migration

I have a HABTM association between Users and Groups in my Rails 3 app. The book I'm following to learn Rails recommends running the following command line to create the join migration:
rails generate migration create_groups_users
However in the documentation it looks like I should've run:
rails generate migration create_groups_users_join_table
So that the following would be in my _create_groups_users.rb migration:
class CreateGroupsUsersJoinTable < ActiveRecord::Migration
Is adding join_table required?
Adding join_table at the end is not explicitly required. Your first command 'create_groups_users' is fine. I've done this in rails 3.0.9 and it works.
You can double check by opening up the migration file and checking that it looks like:
create_table :groups_users, :id => false do |t|
t.integer :group_id
t.integer :user_id
end
The :id => false is needed for a join table as it shouldn't have its own id field.
The last argument in your call to rails generate migration create_groups_users just denotes the class name and a part of the file name of the migration. So it does help to find the migration you have created, the migration file itself is (inside the class body) empty. So both versions are ok.

Adding a column to an existing table in a Rails migration

I have a Users model which needs an :email column (I forgot to add that column during the initial scaffold).
I opened the migration file and added t.string :email, did rake db:migrate, and got a NoMethodError. Then I added the line
add_column :users, :email, :string
again rake db:migrate, again NoMethodError. Am I missing a step here?
Edit: here's the migration file.
class CreateUsers < ActiveRecord::Migration
def self.up
add_column :users, :email, :string
create_table :users do |t|
t.string :username
t.string :email
t.string :crypted_password
t.string :password_salt
t.string :persistence_token
t.timestamps
end
end
def self.down
drop_table :users
end
end
If you have already run your original migration (before editing it), then you need to generate a new migration (rails generate migration add_email_to_users email:string will do the trick).
It will create a migration file containing line:
add_column :users, email, string
Then do a rake db:migrate and it'll run the new migration, creating the new column.
If you have not yet run the original migration you can just edit it, like you're trying to do. Your migration code is almost perfect: you just need to remove the add_column line completely (that code is trying to add a column to a table, before the table has been created, and your table creation code has already been updated to include a t.string :email anyway).
Use this command on the terminal:
rails generate migration add_fieldname_to_tablename fieldname:string
and
rake db:migrate
to run this migration
Sometimes rails generate migration add_email_to_users email:string produces a migration like this
class AddEmailToUsers < ActiveRecord::Migration[5.0]
def change
end
end
In that case you have to manually an add_column to change:
class AddEmailToUsers < ActiveRecord::Migration[5.0]
def change
add_column :users, :email, :string
end
end
And then run rake db:migrate
You can also do
rake db:rollback
if you have not added any data to the tables.Then edit the migration file by adding the email column to it and then call
rake db:migrate
This will work if you have rails 3.1 onwards installed in your system.
Much simpler way of doing it is change let the change in migration file be as it is.
use
$rake db:migrate:redo
This will roll back the last migration and migrate it again.
To add a column I just had to follow these steps :
rails generate migration add_fieldname_to_tablename fieldname:string
Alternative
rails generate migration addFieldnameToTablename
Once the migration is generated, then edit the migration and define all the attributes you want that column added to have.
Note: Table names in Rails are always plural (to match DB conventions). Example using one of the steps mentioned previously-
rails generate migration addEmailToUsers
rake db:migrate
Or
You can change the schema in from db/schema.rb, Add the columns you want in the SQL query.
Run this command: rake db:schema:load
Warning/Note
Bear in mind that, running rake db:schema:load automatically wipes all data in your tables.
You can also add column to a specific position using before column or after column like:
rails generate migration add_dob_to_customer dob:date
The migration file will generate the following code except after: :email. you need to add after: :email or before: :email
class AddDobToCustomer < ActiveRecord::Migration[5.2]
def change
add_column :customers, :dob, :date, after: :email
end
end
You also can use special change_table method in the migration for adding new columns:
change_table(:users) do |t|
t.column :email, :string
end
When I've done this, rather than fiddling the original migration, I create a new one with just the add column in the up section and a drop column in the down section.
You can change the original and rerun it if you migrate down between, but in this case I think that's made a migration that won't work properly.
As currently posted, you're adding the column and then creating the table.
If you change the order it might work. Or, as you're modifying an existing migration, just add it to the create table instead of doing a separate add column.
You can also do this ..
rails g migration add_column_to_users email:string
then rake db:migrate
also add :email attribute in your user controller ;
for more detail check out http://guides.rubyonrails.org/active_record_migrations.html
You can also force to table columns in table using force: true, if you table is already exist.
example:
ActiveRecord::Schema.define(version: 20080906171750) do
create_table "authors", force: true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
end
You could rollback the last migration by
rake db:rollback STEP=1
or rollback this specific migration by
rake db:migrate:down VERSION=<YYYYMMDDHHMMSS>
and edit the file, then run rake db:mirgate again.