This may not be Devise specific but I'm wondering how to add an additional module to a gem that has already been installed when the initial install didn't include said module? In the case of Devise the migration helper t.confirmable is useful in the initial migration's Self.up method and the whole User table is torn down in the Self.down. My Rails-fu isn't strong enough to uncover what the t.confirmable helper is actually doing...
What happens when the User table already exists and you want to add something like :confirmable or :token_authenticatable? Obviously you can't just create_table(:users) again... so me thinks I want to add_column :users, ... and remove_column :users, ... but how do we go about finding out what needs to happen?
Take a look at Devise::Schema
https://github.com/plataformatec/devise/blob/master/lib/devise/schema.rb
which has this
# Creates confirmation_token, confirmed_at and confirmation_sent_at.
def confirmable
apply_devise_schema :confirmation_token, String
apply_devise_schema :confirmed_at, DateTime
apply_devise_schema :confirmation_sent_at, DateTime
end
and then
https://github.com/plataformatec/devise/blob/master/lib/devise/orm/active_record.rb
def apply_devise_schema(name, type, options={})
column name, type.to_s.downcase.to_sym, options
end
So in your migration just do
add_column :users, :confirmation_token, :string
add_column :users, :confirmed_at, :datetime
add_column :users, :confirmation_sent_at, :datetime
and the opposite for the down..
Your migration:
class DeviseAddConfirmable < ActiveRecord::Migration
def change
change_table(:users) do |t|
t.confirmable
end
add_index :users, :confirmation_token, :unique => true
end
end
Related
I had named the add_column :users, :confirmed_at(confirmation_at), :datetime by accident. So I changed it to the appropriate field name :(confirmed_at)
then I entered : rake db:rollback in command line. it was aborted for some reason with the message below however first my Rails db migrate file code:
class AddConfirmableToDevise < ActiveRecord::Migration
def up
add_column :users, :confirmation_token, :string
add_column :users, :confirmed_at, :datetime
add_column :users, :confirmation_sent_at, :datetime
add_index :users, :confirmation_token, unique: true
end
def down
remove_column :users, :confirmation_token, :confirmed_at, :confirmation_sent_at
end
end
Error after migration:
tzurch:~/workspace (gravitar) $ rake db:migrate:redo
#the error that came up below
_________________________________________________
== 20160902201448 AddFullnameToUser: reverting ================================
-- remove_column(:users, :fullname, :string)
-> 0.0207s
== 20160902201448 AddFullnameToUser: reverted (0.0284s) =======================
== 20160902201448 AddFullnameToUser: migrating ================================
-- add_column(:users, :fullname, :string)
-> 0.0008s
== 20160902201448 AddFullnameToUser: migrated (0.0009s) =======================
== 20160913221959 AddConfirmableToDevise: migrating ===========================
-- add_column(:users, :confirmation_token, :string)
-> 0.0009s
-- add_column(:users, :confirmed_at, :datetime)
-> 0.0005s
-- add_column(:users, :confirmation_sent_at, :datetime)
rake aborted!
StandardError: An error has occurred, this and all later migrations canceled:
SQLite3::SQLException: duplicate column name: confirmation_sent_at: ALTER TABLE "users" ADD "confirmation_sent_at" datetime:
The problem here is the syntax you are using to remove column is wrong.
Correct syntax is remove_column
remove_column(table_name, column_name, type = nil, options = {})
Change down migration with
class AddConfirmableToDevise < ActiveRecord::Migration
def up
add_column :users, :confirmation_token, :string
add_column :users, :confirmed_at, :datetime
add_column :users, :confirmation_sent_at, :datetime
add_index :users, :confirmation_token, unique: true
end
def down
remove_column :users, :confirmation_token
remove_column :users, :confirmed_at
remove_column :users, :confirmation_sent_at
remove_index :users, :confirmation_token
end
end
Or remove the down migration altogether and replace up with change, rails will figure out how to rollback:
class AddConfirmableToDevise < ActiveRecord::Migration
def change
add_column :users, :confirmation_token, :string
add_column :users, :confirmed_at, :datetime
add_column :users, :confirmation_sent_at, :datetime
add_index :users, :confirmation_token, unique: true
end
end
I suppose you have already have confirmation_sent_at field in this table in db
class Advertiser < ActiveRecord::Base
devise :database_authenticatable, :registerable,:recoverable, :rememberable, :trackable, :validatable,:confirmable
acts_as_paranoid
end
I added the Devise gem first without the confirmable option. Then I later added the confirmable option with this migration:
class AddConfirmableToDevise < ActiveRecord::Migration
def up
add_column :advertisers, :confirmation_token, :string
add_column :advertisers, :confirmed_at, :datetime
add_column :advertisers, :confirmation_sent_at, :datetime
add_index :advertisers, :confirmation_token, :unique => true
Advertiser.update_all(:confirmed_at => Time.now)
end
end
When I run migration it gives an error
PG::UndefinedColumn: ERROR: column advertisers.deleted_at does not exist
LINE 1: ...onfirmed_at" = '2015-11-05 06:24:26.513079' WHERE "advertise...
Change the migration file like this:
class AddConfirmableToDevise < ActiveRecord::Migration
def up
add_column :advertisers, :confirmation_token, :string
add_column :advertisers, :confirmed_at, :datetime
add_column :advertisers, :confirmation_sent_at, :datetime
add_index :advertisers, :confirmation_token, unique: true
add_column :advertisers, :deleted_at, :time
execute("UPDATE advertisers SET confirmed_at = NOW()")
end
def down
remove_columns :advertisers, :confirmation_token, :confirmed_at, :confirmation_sent_at
end
end
For further reference see devise
I'm getting the following error when trying to use the :confirmable module of Devise:
NameError (undefined local variable or method `confirmed_at' for #<AdminUser:0x007f27841d0f30>)
My model:
class AdminUser < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable,:recoverable, :rememberable, :trackable, :validatable, :confirmable
after_create { |admin| admin.send_reset_password_instructions }
def password_required?
new_record? ? false : super
end
end
I want to send a confirmation email when AdminUser is created.
It is not enough to merely add the :confirmable option in the model, you'll also need to add the columns required by Devise into your database table.
Assuming you are using a model AdminUser:
class AddConfirmableToDevise < ActiveRecord::Migration
def self.up
add_column :admin_users, :confirmation_token, :string
add_column :admin_users, :confirmed_at, :datetime
add_column :admin_users, :confirmation_sent_at, :datetime
add_column :admin_users, :unconfirmed_email, :string
add_index :admin_users, :confirmation_token, :unique => true
end
def self.down
remove_index :admin_users, :confirmation_token
remove_column :admin_users, :unconfirmed_email
remove_column :admin_users, :confirmation_sent_at
remove_column :admin_users, :confirmed_at
remove_column :admin_users, :confirmation_token
end
end
Simply run the command:
rails g migration add_confirmable_to_devise
db/migrate/YYYYMMDDxxx_add_confirmable_to_devise.rb
class AddConfirmableToDevise < ActiveRecord::Migration
# Note: You can't use change, as User.update_all will fail in the down migration
def up
add_column :users, :confirmation_token, :string
add_column :users, :confirmed_at, :datetime
add_column :users, :confirmation_sent_at, :datetime
add_index :users, :confirmation_token, unique: true
execute("UPDATE users SET confirmed_at = NOW()")
end
def down
remove_columns :users, :confirmation_token, :confirmed_at, :confirmation_sent_at
end
end
For further reference devise
You put :confirmable in your AdminUser model. So you need to have columns for confirmable functions including confirmed_at.
You should have a migration file which Devise generated. In that file, you need to remove comment out for confirmable columns including created_at.
I have a model created called "users" and i created a new migration to add some columns to the users table. Now when i run rake db:migrate, I get the error below b/c it's trying to create the users table again
$ rake db:migrate
== DeviseCreateUsers: migrating ==============================================
-- create_table(:users)
rake aborted!
An error has occurred, all later migrations canceled:
Mysql::Error: Table 'users' already exists: CREATE TABLE `users`.....
Why is it trying to create the table again?
Here's the command i used to create the new migration
$ rails generate migration AddDetailsToUsers home_phone:decimal cell_phone:decimal work_phone:decimal birthday:date home_address:text work_address:text position:string company:string
The new migration looks like this:
class AddDetailsToUsers < ActiveRecord::Migration
def change
add_column :users, :home_phone, :decimal
add_column :users, :cell_phone, :decimal
add_column :users, :work_phone, :decimal
add_column :users, :birthday, :date
add_column :users, :home_address, :text
add_column :users, :work_address, :text
add_column :users, :position, :string
add_column :users, :company, :string
end
end
EDIT
20120511224920_devise_create_users
class DeviseCreateUsers < ActiveRecord::Migration
def change
create_table(:users) do |t|
## Database authenticatable
t.string :email, :null => false, :default => ""
t.string :username, :null => false, :default => ""
t.string :encrypted_password, :null => false, :default => ""
## Recoverable
t.string :reset_password_token
t.datetime :reset_password_sent_at
## Rememberable
t.datetime :remember_created_at
## Trackable
t.integer :sign_in_count, :default => 0
t.datetime :current_sign_in_at
t.datetime :last_sign_in_at
t.string :current_sign_in_ip
t.string :last_sign_in_ip
## Encryptable
# t.string :password_salt
## Confirmable
# t.string :confirmation_token
# t.datetime :confirmed_at
# t.datetime :confirmation_sent_at
# t.string :unconfirmed_email # Only if using reconfirmable
## Lockable
# t.integer :failed_attempts, :default => 0 # Only if lock strategy is :failed_attempts
# t.string :unlock_token # Only if unlock strategy is :email or :both
# t.datetime :locked_at
## Token authenticatable
# t.string :authentication_token
t.timestamps
end
add_index :users, :email, :unique => true
add_index :users, :reset_password_token, :unique => true
# add_index :users, :confirmation_token, :unique => true
# add_index :users, :unlock_token, :unique => true
# add_index :users, :authentication_token, :unique => true
end
end
20120619023856_add_name_to_users
class AddNameToUsers < ActiveRecord::Migration
def change
add_column :users, :first_name, :string
add_column :users, :last_name, :string
end
end
20121031174720_add_details_to_users.rb
class AddDetailsToUsers < ActiveRecord::Migration
def change
add_column :users, :home_phone, :decimal
add_column :users, :cell_phone, :decimal
add_column :users, :work_phone, :decimal
add_column :users, :birthday, :date
add_column :users, :home_address, :text
add_column :users, :work_address, :text
add_column :users, :position, :string
add_column :users, :company, :string
end
end
Rails keeps track of the migrations in the "schema_migrations" table of your database. Unless there is an entry for "20120511224920", which is the Devise migration, it will attempt to run it again, which it appears to already exists.
You can add that manually to the table if that is the case.
The error is saying that it's trying to run the original DeviseCreateUsers migration again and can't because the users table already exists.
To fix this, you can run the down migration for DeviseCreateUsers and then run migrations as normal. You can do that with:
rake db:migrate:down VERSION=20121031XXXXXXXX
rake db:migrate
Where 20121031XXXXXXXX is the date stamp of the migration name. In other words, you'll have a migration named 20120410214815_devise_create_users.rb and you copy the date stamp from the filename and paste it into the command. Here's the Rails Guide on Migrations for reference.
Edit: This is noted in the comments, but just a word of warning. Running the down migration for a table will lose any entries that table has. I assume you're running in development mode, so this shouldn't be a problem. If you're in production, you will need to take extra steps to backup the table data and reload it afterwards, otherwise you're going to have a bad day (or week maybe).
Can you try make a fresh database and then migrate it again:
rake db:drop:all
rake db:create:all
rake db:migrate
So from what I have gathered from this:
You already had a User model
You have a version of this in production
You ran a default rails generate devise:install
You then ran rails generate devise User
I am hoping that:
You use source control
You check code in a lot
NOTE: If not, you are about to learn why you need to do so.
Revert your code to before you generated Devise
Hopefully, you can just create a new sandbox of a point right before generating Devise. If not, copy your project directory and do it by hand. The only other option is manually edit all the files that Devise generated.
Rerun your Devise generation
readd gem 'devise' to your Gemfile
rails generate devise:install
rails generate devise MODEL
Make sure that model does not exist! If you don't you get into the problem you are currently having.
Migrate current users from one model to the other
If you can generate a script to completely move authentication information from your old user model to the new, good for you. If you are using a different hashing algorithm from Devise for your current authentication, then you are going to either invalidate all of their passwords and require your users to create a new password using a confirmation code in their email OR you could migrate users as they log in. The first method is clean, complete, and rude. The second method is ugly, incomplete, and silent. Choose your method however you like.
Edit: You could probably find a way to customize Devise to use your algorithm instead. That would probably be even better, but a little more work and fairly brittle.
Another thing is that your authentication model should not be overloaded with account data. You should have a model that only handles authentication which has_a account data model that stores whatever you might want to track about accounts.
use up and down methods. It will be useful for rollback and running specific migration file.
Please follow the syntax..
class AddDetailsToUsers < ActiveRecord::Migration
def self.up
add_column :users, :home_phone, :decimal
add_column :users, :cell_phone, :decimal
add_column :users, :work_phone, :decimal
add_column :users, :birthday, :date
add_column :users, :home_address, :text
add_column :users, :work_address, :text
add_column :users, :position, :string
add_column :users, :company, :string
end
def self.down
remove_column :users, :home_phone
remove_column :users, :cell_phone
remove_column :users, :work_phone
remove_column :users, :birthday
remove_column :users, :home_address
remove_column :users, :work_address
remove_column :users, :position
remove_column :users, :company
end
end
In this case please try to migrate using version number.
Like rake db:migrate:down VERSION=version number #version number is which version you wants to migrate.
I guess you ran rails generate devise user sometime which generated DeviseCreateUsers. If you have already created User model and users table, you can delete the generated migration file from db/migrate.
Check for some environmental variables that might be supplying an unexpected value for the version of your migration. I found an old question on Stack Overflow (and forgive me if it is way out of date) where db:migrate was destroying the table, instead of applying an existing new migration.
They eventually found that an environmental variable was causing db:migrate to run with a version parameter of "0" which is functionally equivalent to rake db:migrate:down
Is it possible that your situation could be caused by the version being unexpectedly changed to include or match the previous migration DeviseCreateUsers?
just try
in the first file
create_table(:users), :force => true do |t|
this will override any other table
According to what you said you used this command to create a new migration
$ rails generate migration AddDetailsToUsers home_phone:decimal cell_phone:decimal work_phone:decimal birthday:date home_address:text work_address:text position:string company:string
Im not sure if its just a typo but it should be "AddDetailsToUser" and not "Users". Just check again and we will be able to help you. This is for devise generated model. When you mention User, in db it looks for Users.
Ruby on Rails follow linguistic convention.table_name is Plural but model_name is Singular. You have to use model_name in the command you used.
If you want to use table_name then use this
rails g migration add_details_to_users home_phone:decimal......etc
And if you need to do some dirty migrations manually:
class A < ActiveRecord::Migration
def up
add_column :images, :name
end
end
A.new.migrate(:up)
I created the following with the devise rails plugin. How do I go about adding a username string and unique user_id integer to the users table? Also is it necessary to create a users_controller when using devise? Or is that not necessary?
class DeviseCreateUsers < ActiveRecord::Migration
def self.up
create_table(:users) do |t|
t.database_authenticatable :null => false
t.recoverable
t.rememberable
t.trackable
# t.encryptable
# t.confirmable
# t.lockable :lock_strategy => :failed_attempts, :unlock_strategy => :both
# t.token_authenticatable
t.timestamps
end
add_index :users, :email, :unique => true
add_index :users, :reset_password_token, :unique => true
# add_index :users, :confirmation_token, :unique => true
# add_index :users, :unlock_token, :unique => true
# add_index :users, :authentication_token, :unique => true
end
def self.down
drop_table :users
end
end
The guide linked to in the comments will provide good reading on migrations, which you will come to use very frequently. In short, however, you can generate the migration needed by doing:
rails g migration add_username_to_users username:string
Once the migration is created, you can actually add the column by doing:
rake db:migrate
To answer to your last question, no, you don't need to create a controller to use devise
Although you could do if you wanted to have an index or show page for your users. In which case you would need to modify your routes a little