I'm using Rails 4 to build a simple Admin interface for adding and removing users, or CRUD.
I'm currently having Devise installed and have built the basic views and actions.
However after filling out the forms myself, this is what I get:
2 errors prohibited this user from being saved:
Email can't be blank
Password can't be blank
This is what I have currently: (removed registerable since I don't want to have a public registration)
Models > user.rb
class User < ActiveRecord::Base
devise :database_authenticatable,
:recoverable, :rememberable, :trackable, :validatable
end
views > users > new.html.erb
<%= form_for #user do |f| %>
<% if #user.errors.any? %>
<div class="error_explanation">
<h2><%= pluralize(#user.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% #user.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true %>
</div>
<div class="field">
<%= f.label :password %>
<% if #validatable %>
<em>(<%= #minimum_password_length %> characters minimum)</em>
<% end %><br />
<%= f.password_field :password, autocomplete: "off" %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "off" %>
</div>
<div class="actions">
<%= f.submit "Create User" %>
</div>
<% end %>
routes.rb (currently I make new user through "/admin/users/new" )
Rails.application.routes.draw do
devise_for :users
scope '/admin' do
resources :users
end
end
Controllers > users_controller.rb
class UsersController < ApplicationController
def index
#users = User.all
end
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def edit
#user = User.find(params[:id])
end
def create
#user = User.new(params[user_params])
if #user.save
redirect_to #user, :flash => { :success => 'User was successfully created.' }
else
render :action => 'new'
end
end
def update
#user = User.find(params[:id])
if #user.update_attributes(params[user_params])
sign_in(#user, :bypass => true) if #user == current_user
redirect_to #user, :flash => { :success => 'User was successfully updated.' }
else
render :action => 'edit'
end
end
def destroy
#user = User.find(params[:id])
#user.destroy
redirect_to users_path, :flash => { :success => 'User was successfully deleted.' }
end
private
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
end
Does anyone have any thoughts as to what is causing the error?
Thanks :D
Just a note: previously, before I disabled :registerable in devise_modules in user.rb, I can actually perform sign-ups using the default devise view "users/sign_up". However what I want is still to get an admin CRUD system, so I disabled it anyway.
Is it typo in your def create/update:
#user = User.new(params[user_params])
# should be
#user = User.new(user_params)
if #user.update_attributes(params[user_params])
# should be
if #user.update_attributes(user_params)
Related
So summary: I wanted to have two types of users which are teachers and students. They are all users and therefore inherit from the users controller. The problem is no about the controllers or classes existing, it's how to adapt them through a form and whether or not I'm missing anything. I'm not sure how to do a backend to radio buttons and have tried things from previous answers but they result in errors. Also, how do I change routes based on which role they select? I have a registrations controller to override Devise.
Here is my form:
<div class="authform"> <%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :role => 'form'}) do |f| %>
<h2>Sign Up</h2>
<%= devise_error_messages! %>
<div class="form-group">
<%= f.label :name %>
<%= f.text_field :name, :autofocus => true, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
</div>
<div>
<%= f.radio_button :role, 'student' %>
<%= label :role_student, 'Student' %>
<%= f.radio_button :role, 'teacher' %>
<%= label :role_teacher, 'Teacher' %>
</div>
<%= f.submit 'Sign Up', :class => 'button right' %>
<% end %> </div>
I have a migration to add roles to users right here
class AddRoleToUsers < ActiveRecord::Migration
def change
add_column :users, :role, :integer
end
end
This was my original attempt on changing routes when adding roles (from another stack overflow question)
class RegistrationsController < Devise::RegistrationsController
def after_sign_up_path_for(resource)
if params[:user][:role] == 'student'
path_to_route = '/classrooms/index'
elsif [:user][:role] == 'teacher'
path_to_route = '/classrooms/new'
else
path_to_route = '/'
end
redirect_to path_to_route
end
end
Finally, here is my user's controller
class UsersController < ApplicationController
# before_action :authenticate_user!
before_action :logged_in_user, only: [:edit, :update]
before_action :correct_user, only: [:edit, :update]
after_action :verify_authorized
# before_filter :check_role
def show
#user = User.find(params[:id])
authorize #user
#posts = #user.posts
end
def new
#user = User.new
authorize #user
end
#def after_sign_up_path_for(resource)
# if params[:user][:role] == 'student'
# path_to_route = '/classrooms/index'
# elsif [:user][:role] == 'teacher'
# path_to_route = '/classrooms/new'
# else
# path_to_route = '/'
# end
# redirect_to path_to_route
#end
def update
#user = User.find(params[:id])
authorize #user
if #user.update_attributes(secure_params)
redirect_to users_path, :notice => "User updated."
else
redirect_to users_path, :alert => "Unable to update user."
end
end
def destroy
user = User.find(params[:id])
# authorize #user
user.destroy
redirect_to users_path, :notice => "User deleted."
end
def create_user_type
end
private
def secure_params
params.require(:user).permit(:role)
end
def logged_in_user
unless logged_in?
flash[:danger] = "Please log in."
redirect_to login_url
end
end
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless #user == current_user
end
end
I know this is a bit of a long post, but I'd appreciate any help. Thanks so much! I love the rails community.
If you use simple_form you can use a standard input and assign the options to collection.
with simple_form
<%= f.input :role, collection: [:admin, :subscriber, :poster] %>
without simple_form
<%= f.select(:role, User.roles.keys.map {|role| [role.titleize,role]}) %>
user.rb
enum role: [:admin, :subscriber, :poster]
I added roles to my users and after sign up devise automatically send me back to the homepage. I tried many things from this site and past questions but nothing seems to work. Maybe I'm over thinking it, I'm new here. Essentially I want people who sign up to choose their role, if they're a teacher they go to create a new class ('classrooms#new') and students go to chose their class ('classrooms#index'). I've created a separate registrations controller as suggested and that hasn't worked.
Sign up form (new.html.erb)
<div class="authform">
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :role => 'form'}) do |f| %>
<h2>Sign Up</h2>
<%= devise_error_messages! %>
<div class="form-group">
<%= f.label :name %>
<%= f.text_field :name, :autofocus => true, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
</div>
<div>
<%= f.radio_button :role, 'student' %>
<%= label :role_student, 'Student' %>
<%= f.radio_button :role, 'teacher' %>
<%= label :role_teacher, 'teacher' %>
</div>
<%= f.submit 'Sign Up', :class => 'button right' %>
<% end %>
</div>
Here is my registrations controller:
class Devise::RegistrationsController < DeviseController
def after_sign_up_path_for(resource)
if params[:user][:role] == 'student'
redirect_to 'classroom#index'
elsif [:user][:role] == 'teacher'
redirect_to 'classroom#new'
end
end
end
Here is my users controller
class UsersController < ApplicationController
before_action :authenticate_user!
after_action :verify_authorized
before_filter :check_role
def show
#user = User.find(params[:id])
authorize #user
#posts = #user.posts
end
def new
#user = User.new
authorize #user
end
def check_role
if params[:user][:role] == 'student'
redirect_to 'classroom#index'
elsif [:user][:role] == 'teacher'
redirect_to 'classroom#new'
end
end
def update
#user = User.find(params[:id])
authorize #user
if #user.update_attributes(secure_params)
redirect_to users_path, :notice => "User updated."
else
redirect_to users_path, :alert => "Unable to update user."
end
end
def destroy
user = User.find(params[:id])
# authorize #user
user.destroy
redirect_to users_path, :notice => "User deleted."
end
def create_user_type
end
private
def secure_params
params.require(:user).permit(:role)
end
end
Edit: Here is my routes page
Rails.application.routes.draw do
get 'students/new'
get 'classrooms/new'
get 'teachers/new'
devise_for :users
resources :users
get "edit_profile" => "devise/registrations#edit"
resources :posts
resources :teachers
post 'users/create_user_type' => 'users/create_user_type'
root to: 'visitors#index'
end
I know there is repetitive code with checking roles, I've just been merging answers together (which isn't good I know)
use pry gem with binding.pry to check your code & variable content.
Anyway, I suppose that role is an attribute of user so why aren't you checking
user.teacher?
user.student?
instead of
params['']['']
resource attribute is persistent instead of params content
I think your registrations controller isn't correct.
If you create your own registrations controller, you want it to override the registrations controller from devise:
class RegistrationsController < Devise::RegistrationsController
def after_sign_up_path_for(resource)
if params[:user][:role] == 'student'
redirect_to 'classroom#index'
elsif [:user][:role] == 'teacher'
redirect_to 'classroom#new'
end
end
end
Then in your routes you must make sure that it also uses your own registrations controller:
devise_for :users, controllers: {registrations: 'registrations'}
Hope this will help you.
I've created a sign up form on rails, and upon clicking my submit button to save my user details, I get the following error:
ActiveModel::ForbiddenAttributesError
for this line: #user = User.new(params[:user])in my users controller.
See code below:
users_controller.rb
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
flash[:notice] = "You signed up successfully"
flash[:color]= "valid"
else
flash[:notice] = "Form is invalid"
flash[:color]= "invalid"
end
render "new"
end
end
new.html.erb
<%= form_for(:user, :url => {:controller => 'users', :action => 'create'}) do |f| %>
</br> <%= f.text_field :firstname, placeholder: 'First Name' %>
</br> <%= f.text_field :lastname, placeholder: 'Last Name' %>
</br> <%= f.text_field :email, placeholder: 'Email' %>
</br> <%= f.password_field :password, placeholder: 'Password' %>
</br> <%= f.password_field :password_confirmation, placeholder: 'Confirm Password' %>
<%= f.submit :Register %>
<% end %>
<% if #user.errors.any? %>
<ul class="Signup_Errors">
<% for message_error in #user.errors.full_messages %>
<li>* <%= message_error %></li>
<% end %>
</ul>
<% end %>
</div>
routes.rb
Rails.application.routes.draw do
get 'users/new'
get 'pages/home'
get 'pages/howitworks'
get 'pages/about'
get 'pages/contact'
get 'pages/becomeauser'
get 'signup' => 'users#new'
resources :users
The params[:user] hash might have attributes in it that isn't a part of the User model. In your case, i'm guessing its password_confirmation.
Use strong params the next time - here
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
flash[:notice] = "You signed up successfully"
flash[:color]= "valid"
else
flash[:notice] = "Form is invalid"
flash[:color]= "invalid"
end
render "new"
end
end
private:
def user_params
params.require(:user).permit(:first_name, :last_name, :email, : password)
end
This means that you have Strong Parameters enabled. I would encourage you to read at least a part of this Rails Guide. Essentially, instead of using params[] to update or create records, you should use an intermediate method to ensure that users aren't sneaking information into your requests. This is much safer. For instance, someone could submit a "created_at" attribute with your form, and then be modifying fields which are not supposed to be edited. Your approach should instead be as follows:
Create a private user_params method in the controller which permits specified attributes
private
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :password, :password_confirmation)
end
Replace your User.create(), User.new(), and User.update() references to params[:user] with user_params
# Do this instead of #user = User.new(params[:user])
#user = User.new(user_params)
I'm having a trouble with my logging in. It highlights the user_params function as an error in users controller. What could it be?
/views/sessions/new.html.erb or login form
<h1>Log In</h1>
<%= form_tag users_path do %>
<div class="field">
<%= label_tag :email %><br />
<%= text_field :email, params[:email] %>
</div>
<div class="field">
<%= label_tag :password %><br />
<%= password_field_tag :password %>
</div>
<div class="actions"><%= submit_tag "Log In" %></div>
<% end %>
controllers/sessions_controller.rb
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by_email(params[:email])
if user && user.authenticate(params[:password])
session[:user_id] = user.id
redirect_to root_url, notice: "Logged In!"
else
flash.now.alert = "Email or password is invalid"
render "new"
end
end
def destroy
session[:user_id] = nil
redirect_to signup_path, notice: "Logged out!"
end
end
controllers/users_controller.rb
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
session[:user_id] = #user.id
redirect_to user_tasks, notice: "Thank you for signing up!"
else
render "new"
end
end
def index
#users = User.all
end
def show
#user = User.find(params[:id])
end
private
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :password, :password_confirmation)
end
end
controllers/application_controller.rb
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
private
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
helper_method :current_user
def authorize
redirect_to login_url, alert: "Not authorized" if current_user.nil?
end
end
rake routes
Generated parameters
{"utf8"=>"✓", "authenticity_token"=>"jtdA0rITCiFjU9ECiKnDUMh/MHMpqv6u+Bd65PAfpARwYsmb9Dli32wpULfmYa7wpBQiFYxAGHF6flCfJgPOew==", "email"=>"dsadsa", "password"=>[FILTERED]", "commit"=>"Log In"}
Any help?
change this line and try please.
<%= text_field :email, params[:email] %>
to
<%= text_field_tag :email, params[:email] %>
Also majorly add this in your routes file to redirect it to sessions controller create action.
post '/authenticate' => 'sessions#create'
change this tag
<%= form_tag users_path do %>
to
<%= form_tag /authenticate do %>
When you look into the params, they are not coming from the user. Try changing your user_params like below
def user_params
params.permit(:first_name, :last_name, :email, :password, :password_confirmation)
end
I suggest you keep the :user namespace for params in your form
<%= form_tag users_path do |form| %>
<%= fields_for :user do |f| %>
<div class="field">
<%= f.label :email %><br />
<%= f.text_field :email, params[:email] %>
</div>
<div class="field">
<%= f.label :password %><br />
<%= f.password_field :password %>
</div>
<div class="actions"><%= submit_tag "Log In" %></div>
<% end %>
<% end %>
So you can still use params.require(:user) in your controller as usual
Parameters will look like this
{
"utf8"=>"✓",
"authenticity_token"=>"jtdA0rITCiFjU9ECiKnDUMh/MHMpqv6u+Bd65PAfpARwYsmb9Dli32wpULfmYa7wpBQiFYxAGHF6flCfJgPOew==",
"user" => {
"email"=>"dsadsa",
"password"=>[FILTERED],
},
"commit"=>"Log In"
}
I get the following error with the supplied code.
undefined method `password' for #<User:0x00000002e66980>
User Model
class User < ActiveRecord::Base
validates_presence_of :username
validates_presence_of :first_name
validates_presence_of :last_name
validates_presence_of :email
validates_uniqueness_of :username
validates_uniqueness_of :email
attr_accessor :password_confirmation
validates_confirmation_of :password
validate :password_non_blank
# password is a virtual attribute
def password=(pwd)
#password = pwd
return if pwd.blank?
create_new_salt
self.hashed_password = self.class.encrypt_password(pwd, self.password_salt)
end
def self.authenticate(username, password)
if user = self.find_by_username(username)
if user.hashed_password == encrypt_password(password, user.password_salt)
user
end
end
end
def User.encrypt_password(password, password_salt)
string_to_hash = password + "woozlewozzle" + password_salt
Digest::SHA1.hexdigest(string_to_hash)
end
private
def password_non_blank
errors.add(:password, "Password cannot be blank.") if hashed_password.blank?
end
def create_new_salt
self.password_salt = self.object_id.to_s + rand.to_s
end
end
Users Controller
class Admin::UsersController < Admin::AdminController
skip_before_filter :authorize
# GET /users
# GET /users.xml
def index
#users = User.find(:all, :order => :username)
respond_to do |format|
format.html # index.html.erb
end
end
# GET /users/1
# GET /users/1.xml
def show
#user = User.find(params[:id])
respond_to do |format|
format.html # show.html.erb
end
end
# GET /users/new
# GET /users/new.xml
def new
#user = User.new
respond_to do |format|
format.html # new.html.erb
end
end
# GET /users/1/edit
def edit
#user = User.find(params[:id])
end
# POST /users
# POST /users.xml
def create
#user = User.new(params[:user])
respond_to do |format|
if #user.save
flash[:notice] = "User #{#user.first_name} #{#user.last_name} was created successfully."
format.html { redirect_to(:action => 'index') }
else
format.html { render :action => 'new' }
end
end
end
# PUT /users/1
# PUT /users/1.xml
def update
#user = User.find(params[:id])
respond_to do |format|
if #user.update_attributes(params[:user])
flash[:notice] = "User #{#user.first_name} #{#user.last_name} was updated successfully."
format.html { redirect_to(:action => 'index') }
else
format.html { render :action => "edit" }
end
end
end
# DELETE /users/1
# DELETE /users/1.xml
def destroy
#user = User.find(params[:id])
#user.destroy
respond_to do |format|
format.html { redirect_to(admin_users_url) }
end
end
end
Namespace Routes
namespace 'admin' do
get 'admin' => 'manage#index'
resources :users
end
User Form
<%= form_for(#user, :url => admin_users_path(#user)) do |f| %>
<% if #user.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#user.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% #user.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :username %><br />
<%= f.text_field :username %>
</div>
<div class="field">
<%= f.label :first_name %><br />
<%= f.text_field :first_name %>
</div>
<div class="field">
<%= f.label :last_name %><br />
<%= f.text_field :last_name %>
</div>
<div class="field">
<%= f.label :salutation %><br />
<%= f.text_field :salutation %>
</div>
<div class="field">
<%= f.label :email %><br />
<%= f.text_field :email %>
</div>
<div class="field">
<%= f.label :password %><br />
<%= f.password_field :password %>
</div>
<div class="field">
<%= f.label :password_confirmation, 'Confirm Password' %><br />
<%= f.password_field :password_confirmation %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Yes, you need attr_accessor :password and you don't need attr_accessor :password_confirmation as validates_confirmation_of :password will create it for you.