I pushed my to do list application to github.
Nor users, nor admins are created. The db is not uploaded to github (and should not).
so I should add all initial setup to db/seeds.rb to ensure that all db expectations are in place.
In addition, the Roles are not created in the seeds file, so the application fails to register users.
I tried to add all initial setup to db/seeds.rb:
Role.create({name: "Admin"})
Role.create({name: "Woeker"})
user1 = User.create!(
email: "admin2#gmail.com",
password: "12345678",
password_confirmation: "12345678"
encrypted_password: "$2a$10$7aK4tZTsCDB64qQI/kl.d.nZGwjEJPh7YlUNE8/Ty.0JhAMS.ALX6"
role_ids = [1]
)
user2 = User.create!(
email: "worker2#gmail.com",
password: "12345678",
password_confirmation: "12345678"
encrypted_password: "$2a$10$7aK4tZTsCDB64qQI/kl.d.nZGwjEJPh7YlUNE8/Ty.0JhAMS.ALX6"
role_ids = [2]
)
(the encrypted_password was taken from the rails console: u = user.last..)
Unfortunately, I'm not sure If I added all what I have to, and if I did it exactly.
in the page of localhost:3000/users/sign_up, I have to enter: Email, Password and Password confirmation.
These are the migrations:
class DeviseCreateUsers < ActiveRecord::Migration
def change
create_table(:users) do |t|
## Database authenticatable
t.string :email, :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
t.timestamps
end
add_index :users, :email, :unique => true
add_index :users, :reset_password_token, :unique => true
end
end
class Roles < ActiveRecord::Migration
def self.up
create_table :roles do |t|
t.string :name
t.timestamps
end
end
def self.down
drop_table :roles
end
end
class UserRoles < ActiveRecord::Migration
def self.up
create_table :roles_users, :id => false do |t|
t.references :role, :user
end
end
def self.down
drop_table :roles_users
end
end
Any help appreciated!
UPDATE: THIS IS THE SOLUTION?
Role.create({name: "Admin"})
Role.create({name: "Woeker"})
user1 = User.create!(
email: "admin2#gmail.com",
password: "12345678",
password_confirmation: "12345678"
role_ids = [1]
)
user2 = User.create!(
email: "worker2#gmail.com",
password: "12345678",
password_confirmation: "12345678"
role_ids = [2]
)
I have something like that. Remove the password confirmation. Do exactly as if you were in the sign in form.
Related
I'm trying to seed an application database, but I'm getting the following error:
ActiveRecord::AssociationTypeMismatch: Role(#97332160) expected,
got Fixnum(#76482890)
Here is the part of my schema.rb concerning the two table related to the problem:
create_table "roles", force: :cascade do |t|
t.string "role", limit: 50, null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "users", force: :cascade do |t|
t.string "first_name", limit: 100, null: false
t.string "surname", limit: 100, null: false
t.string "email", limit: 255
t.integer "role_id", limit: 11
t.string "password", limit: 150
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "users", ["first_name"], name: "index_users_on_first_name", using: :btree
add_index "users", ["role_id"], name: "index_users_on_role_id", using: :btree
add_index "users", ["surname"], name: "index_users_on_surname", using: :btree
add_foreign_key "users", "roles"
And here are my seeds.rb commands:
rl = Role.create(role: "root")
User.create(first_name: "Edvaldo", surname: "Silva de Almeida Júnior", email: "edvaldo#my.domain.com", role_id: rl.id, password: "my_password")
rl = Role.create(role: "admin")
User.create(first_name: "Daiely", surname: "Fanchin", email: "daiely#my.domain.com", role_id: rl.id, password: "other_password")
rl = Role.create(role: "user")
User.create(first_name: "César", surname: "Silva", email: "cesar#my.domain.com", role_id: rl.id, password: "yet_other_password")
I found a question where the accepted answer suggested I should do something like:
rl = Role.create(role: "root")
User.create(first_name: "Edvaldo", surname: "Silva de Almeida Júnior", email: "edvaldo#my.domain.com", role_id: Role.find(rl.id), password: "my_password")
I tried that, but got the same error! Besides, I'm using the role_id as association, not the role itself. So, as far as I'm concerned, it should receive a Fixnum, not a Role.
add_foreign_key "users", "roles" this will add role_id column to users no required to separately write t.integer "role_id", limit: 11
there should be some associations
class User < ActiveRecord::Base
belongs_to :role
end
and
create_table "users", force: :cascade do |t|
t.string "first_name", limit: 100, null: false
t.string "surname", limit: 100, null: false
t.string "email", limit: 255
t.reference "role", index: true
t.string "password", limit: 150
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_foreign_key "users", "roles", column: :role_id
hope it might solve it.
I had added a method set_default_role to my User model.
class User < ActiveRecord::Base
has_one :role
after_initialize :set_default_role, :if => :new_record?
def set_default_role
if User.count == 0
self.role ||= 1
else
self.role ||= 2
end
end
end
But this method was wrong and as soon as I rewrote it as
def set_default_role
if User.count == 0
self.role_id ||= 1
else
self.role_id ||= 2
end
end
my seeding worked fine.
So, the problem was the fact I was tryind (inside this method) to set a Role to a Fixnum.
This serves as an advice to Rails newbies: Seeding is not just forcing something to your database. It is a full evaluation of your model and sometimes an error may be caused by the execution of some method, not just by the values you try to load.
The problem you have is simple:
role_id: Role.find(rl.id)
.find() returns a Role object.
Your create method is expecting a foreign_key (integer) (role_id: [int]).
The simple fix would be:
User.create(... role_id: Role.find(rl.id).id ...)
.. or ...
User.create(... role_id: rl.id ...)
... or even better ...
User.create(... role: rl ...)
--
Fix
The real fix would either to set a default in your database, or manually set the role_id, as you have in your submitted answer.
I would strong recommend using the database default because then it doesn't matter what happens in your app - your database will always "default" to the role you set:
$ rails g migration SetDefaultForRole
$ db/migrate/set_default_for_role_____.rb
class SetDefaultForRole < ActiveRecord::Migration
def change
change_column :users, :role_id, :integer, default: 0
end
end
$ rake db:migrate
The downside to this would be that this integer default would be set (unless you changed your db again). Whilst not a problem in itself, it would create an antipattern for your Role model (IE you can create any role, as long as it's after the initial one you made).
--
The other way would be to set the Role for the user.
You have this already; you could clean it up:
#app/models/user.rb
class User < ActiveRecord::Base
before_create :set_role, if: Proc.new { |m| m.role_id.blank? }
private
def set_role
self.role_id = "1" if User.count == 0
end
end
I need use Devise with a existing user table in another database, so I config my database.yml:
development:
adapter: postgresql
encoding: unicode
database: almoxarifado
pool: 5
timeout: 5000
username: user
password: pass
host: localhost
users_development:
adapter: postgresql
encoding: unicode
database: portal
pool: 5
timeout: 5000
username: user
password: pass
host: localhost
schema_search_path: users
In my model I wrote this code:
class User < ActiveRecord::Base
establish_connection "users_#{Rails.env}"
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
When I run Rails c and call model class this work very well.
A migration file:
class AddDeviseToUsers < ActiveRecord::Migration
def self.up
ActiveRecord::Base.establish_connection "users_#{Rails.env}"
change_table(:users) do |t|
## Database authenticatable
t.string :email, :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, :null => false
t.datetime :current_sign_in_at
t.datetime :last_sign_in_at
t.string :current_sign_in_ip
t.string :last_sign_in_ip
## 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, :null => false # Only if lock strategy is :failed_attempts
# t.string :unlock_token # Only if unlock strategy is :email or :both
# t.datetime :locked_at
# Uncomment below if timestamps were not included in your original model.
# 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
ActiveRecord::Base.establish_connection "#{Rails.env}"
end
def self.down
# By default, we don't want to make any assumption about how to roll back a migration when your
# model already existed. Please edit below which fields you would like to remove in this migration.
raise ActiveRecord::IrreversibleMigration
end
end
But rake db:migrate return this error:
== AddDeviseToUsers: migrating =======================================
-- change_table(:users)
rake aborted!
An error has occurred, this and all later migrations canceled:
undefined method `rollback' for #<ActiveRecord::ConnectionAdapters::ClosedTransaction:0xbb06767c>
Tasks: TOP => db:migrate
(See full trace by running task with --trace)
Since I spent a lot of time trying with all alternatives in this and other posts, without success, here the solution that works to me
database.yml
external_database:
adapter: postgresql
database: <%= ENV['DATABASE_NAME'] %>
username: <%= ENV['DATABASE_USERNAME'] %>
password: <%= ENV['DATABASE_PASSWORD'] %>
host: <%= ENV['DATABASE_HOST'] %>
port: <%= ENV['DATABASE_PORT'] || 5432 %>
models/yourmodel.rb
class YourModel < ActiveRecord::Base
establish_connection :external_database
end
your migration
class YourMigration < ActiveRecord::Migration
def self.up
YourModel.connection.enable_extension "plpgsql"
YourModel.connection.create_table :your_table_name, id: :uuid do |t|
t.text :title
...
end
end
def self.down
YourModel.connection.drop_table :your_table_name
end
end
Explanation:
Since the ActiveRecord::Base.establish_connection closes the connection, I'm using the connection from the model directly. Also, since I'm using UUID with the method uuid_generate_v4() as default, I enabled the extension using the same connection
=================================UPDATE=================================
I find a nice article talking about it: http://www.thegreatcodeadventure.com/managing-multiple-databases-in-a-single-rails-application/
=================================UPDATE=================================
I just put establish_connection out of a migrate class.
ActiveRecord::Base.establish_connection "users_#{Rails.env}"
class AddDeviseToUsers < ActiveRecord::Migration
def self.up
change_table(:users) do |t|
## Database authenticatable
t.string :email, :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, :null => false
t.datetime :current_sign_in_at
t.datetime :last_sign_in_at
t.string :current_sign_in_ip
t.string :last_sign_in_ip
## 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, :null => false # Only if lock strategy is :failed_attempts
# t.string :unlock_token # Only if unlock strategy is :email or :both
# t.datetime :locked_at
# Uncomment below if timestamps were not included in your original model.
# 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
end
def self.down
# By default, we don't want to make any assumption about how to roll back a migration when your
# model already existed. Please edit below which fields you would like to remove in this migration.
raise ActiveRecord::IrreversibleMigration
end
end
Create a method in your migration class
def self.connection
ActiveRecord::Base.establish_connection("#{Rails.env}_yourcustomenv").connection
end
took me some time to realize a method declared with self was needed. This worked on Rails 4.0
You were almost at the solution.
Just had to do User.connection instead of ActiveRecord::Base.establish_connection.
Take a look at the solution here:
https://stackoverflow.com/a/34292909/2499227
The ActiveRecord::Base.establish_connection way always gave me issues with schema_migrations and/or connection closing, but the Model connection runs smoothly.
In your config/database.yml :
development_custom_db:
database: almoxarifado
username: user
password: pass
host: localhost
Create a file contain this , in the path config/initializers/ :
module Custom
class Base < ActiveRecord::Base
self.abstract_class = true
connection_name = "#{Rails.env}_custom_db"
if Rails.application.config.database_configuration.has_key?(connection_name)
establish_connection connection_name.to_sym
end
end
class Migration < ActiveRecord::Migration
def connection
#_connection ||= Custom::Base.connection
end
end
end
Then inherit it at your migration file:
class AddDeviseToUsers < Custom::Migration
def up
change_table(:users) do |t|
end
end
end
Used a bit simpler approach and it worked for me on Rails 6.
class AddDimensionsToReportRecords < ActiveRecord::Migration[6.0]
def change
add_column :report_records, :sample_length, :decimal
add_column :report_records, :sample_height, :decimal
add_column :report_records, :sample_width, :decimal
end
def connection
ReportRecord.connection
end
end
And the model...
class ReportRecord < ApplicationRecord
connects_to database: { writing: :reports_db, reading: :reports_db_replica }
end
When I attempt to do an #user.update_attributes(params[:user]) my user is logged out, or what appears to be logged out. I get the message, undefined method 'first_name' for nil:NilClass. If I go back to my home page I get the log in link. Why is this happening? I've read on stackoverflow that I need to include attr_accessible :admin but that didn't seem to help at all.
user.rb update method
def update
#user = User.find(params[:id])
if !params[:headshoturl].blank? then
#user.upload_headshot(params[:headshoturl])
end
respond_to do |format|
if #user.update_attributes(params[:user])
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
Migration File
class DeviseCreateUsers < ActiveRecord::Migration
def change
create_table(:users) do |t|
## Database authenticatable
t.string :email, :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
## 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
# Custom changes
t.string :first_name
t.string :last_name
t.string :user_code
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
routes.rb
devise_for :users do get '/users/sign_out' => 'devise/sessions#destroy' end
resources :users
Does params[:user] include a password and password_confirmation? When you update a user's password, Devise signs them out. You can prevent this by writing your own RegistrationsController and doing sign_in #user, :bypass => true after updating your user.
About the first_name error, could you make sure that first_name appears in schema.rb? I'm thinking maybe you added it to the DeviseCreateUsers migration after the migration had already been run. If that's the case you'd want to create a new migration, create first_name and whatever other fields in it, then do a db:migrate.
In my app i need to do two way-login system:
1) User - only for user part of website, contain's information about user's, it's login data, etc...
2) Admin - another model for admin part of website.
But how to do this?
Now i have only first part, and my migration:
class DeviseCreateUsers < ActiveRecord::Migration
def self.up
create_table(:users) do |t|
## Database authenticatable
t.string :email, :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
def self.down
drop_table :users
end
end
And route:
devise_for :users
But how to add second admin-login part?
You have 3 options:
Add string field type to your table and derive from User model (the admin and user will have the same login route).
Add boolean field admin and use i.e. CanCan gem.
Run rails g devise admin and have separate admin model.
The way you choose depends on your app struct.
I am using authlogic to do my authentication. The current model that serves as the authentication model is the user model. I want to add a "belongs to" relationship to user which means that I need a foreign key in the user table. Say the foreign key is called car_id in the user's model. However, for some reason, when I do
u = User.find(1)
u.car_id = 1
u.save!
I get
ActiveRecord::RecordInvalid: Validation failed: Password can't be blank
My guess is that this has something to do with authlogic. I do not have validation on password on the user's model. This is the migration for the user's table.
def self.up
create_table :users do |t|
t.string :email
t.string :first_name
t.string :last_name
t.string :crypted_password
t.string :password_salt
t.string :persistence_token
t.string :single_access_token
t.string :perishable_token
t.integer :login_count, :null => false, :default => 0 # optional, see Authlogic::Session::MagicColumns
t.integer :failed_login_count, :null => false, :default => 0 # optional, see Authlogic::Session::MagicColumns
t.datetime :last_request_at # optional, see Authlogic::Session::MagicColumns
t.datetime :current_login_at # optional, see Authlogic::Session::MagicColumns
t.datetime :last_login_at # optional, see Authlogic::Session::MagicColumns
t.string :current_login_ip # optional, see Authlogic::Session::MagicColumns
t.string :last_login_ip # optional, see Authlogic::Session::MagicColumns
t.timestamps
end
end
And later I added the car_id column to it.
def self.up
add_column :users, :user_id, :integer
end
Is there anyway for me to turn off this validation?
Sure. Per the docs:
acts_as_authentic do |c|
c.ignore_blank_passwords = true
end