Rails change_column removing index from schema - mysql

I am trying to make it so certain columns cannot be null. I use change_column no problem but some of these columns have an index attached to them signifying that it is unique.
However when I run this migration:
change_column :users, :username, :string, null: false
change_column :users, :email, :string, null: false
change_column :users, :password, :string, null: false
change_column :users, :terms_agreed, :boolean, null: false
It removes the add_index in the schema
schema.rb
- add_index "users", ["username"], :name => "index_users_on_username_code", :unique => true
- add_index "users", ["email"], :name => "index_users_on_email_code", :unique => true
add_index "users", ["confirmation_code"], :name => "index_users_on_confirmation_code", :unique => true
How do I do this without removing the indexes?
P.S. its not actually removing the indexes in the database. Just in the schema.rb file.

The behaviour of how migrations are treated depends on you database implementation. More info here, but in your migration you should explicitly request an index in your change.
class DoSomethingToTable < ActiveRecord::Migration
def change
change_column :users, :username, :string, null: false, index: true
end
end
See the docs for more information.

So still unclear as to what happened, but turns out that the indexes got deleted from the database during some database manipulation.
I just put the schema back the way I needed it to be and ran rake db:setup then my migration again and everything worked fine. Very confusing and a lot of unnecessary time spent trying to figure it out.

Related

MySQL Syntax error: unexpected keyword_do_block

I am very new to Ruby and trying to complete a tutorial that I can't get to work properly. I am attempting to run rake db:migrate on my root folder and is giving me 3 separate error messages:
>rake db:migrate
rake aborted!
SyntaxError:
C:/Users/Bill/Sites/simple_cms/db/migrate/20170922050429_create_use
rs.rb:4: syntax error, unexpected keyword_do_block
create_table :users, do |t|
^
C:/Users/Bill/Sites/simple_cms/db/migrate/20170922050429_create_users.rb:5:
syntax error, unexpected tSTRING_BEG, expecting keyword_end
t.column "first_name", :string, :limit => 25
^
C:/Users/Bill/Sites/simple_cms/db/migrate/20170922050429_create_users.rb:5:
syntax error, unexpected ',', expecting keyword_end
t.column "first_name", :string, :limit => 25
^
Tasks: TOP => db:migrate
(See full trace by running task with --trace)
The Ruby code from my create_users.rb file is:
class CreateUsers < ActiveRecord::Migration[5.1]
def change
create_table :users, do |t|
t.column "first_name", :string, :limit => 25
t.string "last_name", :limit => 50
t.string "email", :default => "", :null => false
t.string "password", :limit => 40
t.timestamps
end
end
I am not sure what I am doing wrong here. Any insight would be appreciated!
in addition to first answer. Why don't you use rails generators?
Write migrations can be usefull when you have to modify something. Faster and cleaner way is using rails generators:
Example:
rails g model User first_name:string last_name:string
If attributes are string you can just
rails g model User first_name last_name
It will generate class User in /app/models/user.rb and migration for database.
Also you have Scaffold, and others generators.
More info: Command Line Rails
Tip: Check for Devise Gem, it will generate entire structure for User Model.
Link: Devise Gem
In your code:
def change
create_table :users, do |t|
t.column "first_name", :string, :limit => 25
t.string "last_name", :limit => 50
t.string "email", :default => "", :null => false
t.string "password", :limit => 40
t.timestamps
end
end
You have:
create_table :users, do |t|
which should be:
create_table :users do |t|
There should not be any comma after that unless you have more than one arguments there.
Update:
And also you class has no end which will not close the class definition and throw exception.
On a side note:
You have this line:
t.column "first_name", :string, :limit => 25
which can be written as:
t.string "first_name", :limit => 25
Hope this helps.

Ruby on Rails - rake db:migrate is not working

I am using def change for models. Here is migration output of my models:
$ rake db:migrate
== 20150802221545 CreateUsers: migrating ======================================
-- create_table(:users)
-> 0.3319s
== 20150802221545 CreateUsers: migrated (0.3322s) =============================
== 20150802221550 CreatePages: migrating ======================================
-- create_table(:pages)
-> 0.2457s
-- add_index(:pages, :subject_id)
-> 0.2487s
-- add_index(:pages, :permalink)
-> 0.2464s
== 20150802221550 CreatePages: migrated (0.7416s) =============================
== 20150802221558 CreateSubjects: migrating ===================================
-- create_table(:subjects)
-> 0.2453s
== 20150802221558 CreateSubjects: migrated (0.2456s) ==========================
== 20150802221603 CreateSections: migrating ===================================
-- create_table(:sections)
-> 0.2776s
-- add_index(:sections, :page_id)
-> 0.2484s
== 20150802221603 CreateSections: migrated (0.5265s) ==========================
Now, I created a migration named AlterUsers and wrote following code.
- class AlterUsers < ActiveRecord::Migration
def change
reversible do |dir|
dir.up {
rename_table :users, :admin_users
add_column :admin_users, :username, :string, :limit => 25
#add_column("admin_users", "username", :string, :limit => 25)
change_column :admin_users, :email, :string, :limit => 100
#change_column("admin_users", "email", :string, :limit => 100)
rename_column :admin_users, :password, :hashed_password
#rename_column("admin_users", "password", "hashed_password")
add_column :admin_users, :salt, :string, :limit => 40
#add_column("admin_users", "salt", :string, :limit => 40)
puts "*** about to add index ***"
add_index :admin_users, :username
#add_index("admin_users", "username")
}
dir.down{
remove_index :admin_users, :username
remove_column :admin_users, :salt
rename_column :admin_users, :hashed_password, :password
change_column :admin_users, :email, :string, :default=> "", :null => false
remove_column :admin_users, :username, :string, :limit => 25
raname_table :admin_users, :users
}
end
end
end
I used def change as I am using rails4. But rake db:migrate doesn't affect database. Then I wrote same code as instructor's code:
class AlterUsers < ActiveRecord::Migration
def self.up
rename_table :users, :admin_users
add_column :admin_users, :username, :string, :limit => 25
change_column :admin_users, :email, :string, :limit => 100
rename_column :admin_users, :password, :hashed_password
add_column :admin_users, :salt, :string, :limit => 40
puts "*** about to add index ***"
add_index :admin_users, :username
end
def self.down
remove_index :admin_users, :username
remove_column :admin_users, :salt
rename_column :admin_users, :hashed_password, :password
change_column :admin_users, :email, :string, :default=> "", :null => false
remove_column :admin_users, :username, :string, :limit => 25
raname_table :admin_users, :users
end
end
It worked. After all these migrations, I am trying to undo by using this command rake db:migrate VERSION=0. But it's not working. Here is db migration status:
$ rake db:migrate:status
database: mysqlapp_development
Status Migration ID Migration Name
--------------------------------------------------
up 20150802221545 Create users
up 20150802221550 Create pages
up 20150802221558 Create subjects
up 20150802221603 Create sections
up 20150802221735 Alter users
And here is rake db:migrate --trace output
$ rake db:migrate --trace
** Invoke db:migrate (first_time)
** Invoke environment (first_time)
** Execute environment
** Invoke db:load_config (first_time)
** Execute db:load_config
** Execute db:migrate
** Invoke db:_dump (first_time)
** Execute db:_dump
** Invoke db:schema:dump (first_time)
** Invoke environment
** Invoke db:load_config
** Execute db:schema:dump
Now I have following major queries in addition to above problem.
1) In which cases rake db:migrate doesn't affect? (I'm confused because of it's working behaviour)
2) How to redo the undo/roll back migrations?
You should simply split this into several migrations. Just use change and let rails deal with creating the reverse migration.
Given:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :email
t.string :password
t.timestamps null: false
end
end
end
Migrations:
class ChangeUsersTable < ActiveRecord::Migration
def change
rename_table :users, :admin_users
end
end
class AddHashedPasswordToAdminUsers < ActiveRecord::Migration
def change
# you should not just migrate the plaintext passwords!
remove_column :admin_users, :password, :string
add_column :admin_users, :password_hash, :string
add_column :admin_users, :salt, :string, limit: 40
end
end
class AddUserNameToAdminUser < ActiveRecord::Migration
def change
add_column :admin_users, :username, :string
end
end
Result:
ActiveRecord::Schema.define(version: 20150802210303) do
create_table "admin_users", force: :cascade do |t|
t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "password_hash"
t.string "salt", limit: 40
t.string "username"
end
end
Don't roll your own password encryption!
I would also recommend that you use has_secure_password rather than rolling your own password encryption. Its way to easy to mess it up.
In that case the migration would be:
class AddPasswordDigestToAdminUsers < ActiveRecord::Migration
def change
# you should not just migrate the plaintext passwords!
remove_column :admin_users, :password, :string
add_column :admin_users, :password_digest, :string
# has_secure_password concatenates the salt into the digest.
# no salt column needed.
end
end

rake db:migrate not creating table

I'm just following this RoR tut, I'm doing it the same way but I'm stuck creating a table:
$ rails generate model User
invoke active_record
create db/migrate/20140718180319_create_users.rb
create app/models/user.rb
invoke test_unit
create test/models/user_test.rb
create test/fixtures/users.yml
This is my xxxxx_create_users.rb
class CreateUsers < ActiveRecord::Migration
def Up
create_table :users do |t|
t.column "first_name", :string, :limit => 25
t.string "last_name", :limit => 50
t.string "email", :default => "", :null => false
t.string "password", :limit => 40
t.timestamps
end
end
def down
drop_table :users
end
end
When I run db:migrate the table is not being created:
$ rake db:migrate
== 20140718182504 CreateUsers: migrating ======================================
== 20140718182504 CreateUsers: migrated (0.0000s) =============================
Is missing
create_table(:users)
-> x.xxxxxs
What am I doing wrong? Thanks.
Isn't it a typo with your "Up" migration method? Try with:
def up
instead of:
def Up

No such column: admin_users.password

When I run my migrations in rails, I get back a sql error saying that no such column:admin_users.password exists. When I SHOW FIELDS it's clearly there. I'm sure this is because I missed something somewhere. In this case, understanding what went wrong is much more important to me than fixing the issue since I keep getting stuck with my database in a broken state.
First Migration:
class CreateUsers < ActiveRecord::Migration
def up
create_table :users do |t|
t.column "first_name", :string, :limit => 25
t.string "last_name", :limit => 50
t.string "email", :default => "", :null => false
t.string "password:", :limit => 40
# t.datetime "created_at"
# t.datetime "updated_at"
# The above two are created automatically by the below command
t.timestamps
end
end
def down
drop_table :users
end
end
Second Migration:
class AlterUsers < ActiveRecord::Migration
def up
rename_table("users", "admin_users")
add_column("admin_users", "username", :string, :limit => 25, :after => "email")
change_column("admin_users", "email", :string, :limit => 100)
rename_column("admin_users", "password", "hashed_password")
puts "*** Adding an index is next***"
add_index("admin_users", "username")
end
def down
remove_index("admin_users", "username")
rename_column("admin_users", "hashed_password", "password")
change_column("admin_users", "email", :default => "", :null => false)
remove_column("admin_users", "username")
rename_table("admin_users", "users")
end
end
=>== CreateUsers: migrated (0.0163s) ===========================================
== AlterUsers: migrating =====================================================
-- rename_table("users", "admin_users")
-> 0.0034s
-- add_column("admin_users", "username", :string, {:limit=>25, :after=>"email"})
-> 0.0158s
-- change_column("admin_users", "email", :string, {:limit=>100})
-> 0.2273s
-- rename_column("admin_users", "password", "hashed_password")
rake aborted!
An error has occurred, all later migrations canceled:
No such column: admin_users.password
That's because you called your column password: with a colon in the first migration.
I bet that's because of a typo in the rails generate model commandline. It's horribly un-foolproof.

Table doesn't exist: SHOW KEYS

I am getting this error when I run db:migrate
Mysql2::Error: Table 'sample_app_development.microposts' doesn't exist: SHOW KEYS FROM
`microposts
This is my migration
class CreateMicroposts < ActiveRecord::Migration
def change
create_table :microposts do |t|
t.string :content
t.integer :user_id
t.timestamps
add_index :microposts, [:user_id, :created_at]
end
end
end
I have tried restarting mysql and deleting and recreating the database.
Move your add_index out of the create_table's block.