When accessing URL localhost:3000/buyers/sign_up?email=abc%40abc.com , the sign-up form is displayed but email field stays empty and is not auto-filled with abc#abc.com.
Could this be related to the implemented Single Table Inheritance (STI) ? -> Buyer inherits from User.
routes.rb
devise_for :users, :controller => {:sessions => 'sessions'}, :skip => :registrations do
delete '/logout', :to => 'sessions#destroy', :as => :destroy_user_session
get '/login', :to => 'sessions#new', :as => :new_user_session
post '/login', :to => 'sessions#create', :as => :new_user_session
end
devise_for :sellers, :skip => :sessions
devise_for :buyers, :skip => :sessions
resources :users
devise/registrations/new.html.erb
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<div><%= f.label :email %><br />
<%= f.email_field :email, autofocus: true %></div>
<div><%= f.submit "Sign up" %></div>
<% end %>
users_controller
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render action: 'show', status: :created, location: #user }
else
format.html { render action: 'new' }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
#user = User.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit(:first_name, :last_name, :name, :type, :email)
end
end
Just found the answer.
Simply need to add value in the form + a condition to check presence of the e-mail param:
<%= f.email_field :email, value: params[:email] %>
Params
The problem isn't to do with STI - it's to do with how you're passing variables / parameters to your form.
You must remember that when you use form_for, Rails will automatically populate the provided HTML elements with data from the ActiveRecord object - in that if your object has the attribute email, the form will have that data populated
If you want to populate the form with new data (from a URL), you'll either have to be able to populate the #user.email attribute (as suggested by JTG), or take the params from the URL & pass them through to your form
--
The ways I would look at doing this would include:
Controller-based (the best)
Front-end "params" based
The controller method can be accessed as follows:
#app/controllers/users_controller.rb
Class UsersController < ApplicationController
def new
#user = User.new(email: params[:email])
end
end
This should work, as the param should be made available to the controller during the request.
-
The alternative, as you've discovered, is to populate the value of the element on your page:
<%= f.text_field :email, value: params[:email] %>
This will leave the :email field blank if no email parameter was / is defined.
Making a request to new#User (which is what you are requesting with /buyers/sign_up) won't autofill any of the sign_up form by default
However, you can put
def new
#user = User.new
#user.email = params[:email] if params.has_key? "email"
end
for the url
/buyers/sign_up?email=abc%40abc.com
And it should auto complete the email textfield of your form.
if your url was structured like so...
/buyers/sign_up?user[email]=abc%40abc.com
Then you should be able to use the much more elegant
def new
#user = User.new(params[:user])
end
in your new call. That way you can send in more than one parameter
Related
routes are correct, not sure what else is off?
upon "updating" page (when it routes to update) I get the error
param is missing or the value is empty: user
This is my second custom view page for devise the other is working with similar code.
routes-
devise_for :users, controllers: {registrations: 'users/registrations'} as :user do
end
root 'home#home'
get 'users/discovery_settings' => 'users#discovery_settings'
patch 'users/discovery_settings/update' => 'users#update_discovery'
My user controller-
def update_discovery
#user = User.find(current_user.id)
if #user.update(user_params2)
# Sign in the user by passing validation in case their password changed
sign_in #user, :bypass => true
redirect_to root_path
else
render "edit"
end
end
def user_params2
# NOTE: Using `strong_parameters` gem
params.require(:user).permit(:discovery, :male, :female, :min_age, :max_age)
end
Then my views form_for calls the method
<%= form_for(#user, :url => { :action => "update_discovery" }, html: {class: "form floating-label"}) do |f| %>
Figured this out on my own and posted a blog about it, here was my answer:
Out of all the problems i've had customizing devise this one probably took the longest for me to figure out. A common error you will receive from doing this incorrectly is:
param is missing or the value is empty: user
1st step is to create a controller that inherits the devise controller (essentially overriding it). This should go inside controllers/users and be called registration_controller.rb .
Insert the following code for a secondary customer edit view-
class Users::RegistrationsController < Devise::RegistrationsController
before_filter :configure_permitted_parameters
def NAMEOFVIEW
#user = current_user
if #user
render :NAMEOFVIEW
else
render file: 'public/404', status: 404, formats: [:html]
end
end
also you'll need to sanitize any custom parameters you make in your database
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up) { |u| u.permit(:username, :email, :password, :name, :user_age, :user_gender ) }
devise_parameter_sanitizer.permit(:account_update) { |u| u.permit(:username, :email, :password, :password_confirmation, :current_password, :name, :user_age, :user_gender, :user_bio ) }
end
and finally allow updates without password (by default devise forces this)
protected
def update_resource(resource, params)
resource.update_without_password(params)
end
Under routes:
Rails.application.routes.draw do
devise_for :users, controllers: {registrations: 'users/registrations'}
as :user do
match 'users/NAMEOFVIEWHERE' => 'users/registrations#NAMEOFVIEWHERE', :via => [:get], :as => 'NAMEOFVIEWHERE'
then finally any changes in your view can be done using a form_for or a form_tag, form_for example below:
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put, class: " html classes here" }) do |f| %>
<div class="field">
<%= f.label :Bio %><br />
<%= f.text_area :user_bio, autocomplete: "off", class: "optional html class here", rows: 3, cols: 20, name: "description", placeholder: "About" %>
</div>
<div class="optional button class here">
<button type="submit">Save</button>
</div>
<% end %>
Now restart your rails server and cross your fingers!
I am using a form for "GET" method. I created a text field (in testing.html.erb) for user to enter the name. I want the name to be passed into the controller and based on the name, I want to retrieve his data from the database through a query.
The problem here is that I am not getting anything into the instance variable in the controller action (Nothing is printed on screen when I display #testing in "testing.html.erb"). Below is my routes.rb file.
Rails.application.routes.draw do
resources :users
# The priority is based upon order of creation: first created -> highest priority.
# See how all your routes lay out with "rake routes".
# You can have the root of your site routed with "root"
# root 'welcome#index'
# Example of regular route:
get 'index' => 'users#index'
get 'testing' => 'users#testing'
# Example of named route that can be invoked with purchase_url(id: product.id)
# get 'products/:id/purchase' => 'catalog#purchase', as: :purchase
.....
This is my testing.html.erb file
<h1> Testing page </h1>
<%= form_for users_path, :url => {:action => "testing", :name => :name}, :html => {:method => :get} do |f| %>
<%= f.label :first_name %><br />
<%= f.text_field :name %><br />
<%= f.submit "View Schedule" %>
<% end %>
<%= #testing %>
<%#= #testing.email %>
<%#= #testing.content %>
Please note that I commented #testing.email/content in above file to supress the error (undefined method `email' for nil:NilClass).
Below is my users_controller.rb file.
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
#before_action :user_set, only: [:testing]
# GET /users
# GET /users.json
def index
#users = User.all
#test = params[:name]
#test_index = params[:age]
end
# GET /users/1
# GET /users/1.json
def show
end
def testing
#testing = User.find_by(name: params[:name])
#if #testing.name == "Siri"
# #render text: "Hello #{#testing.name}"
#redirect_to action: :index
#end
end
.........
private
# Use callbacks to share common setup or constraints between actions.
def set_user
#user = User.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit(:name, :email, :content)
end
end
The log-file shows the following.
Processing by UsersController#testing as HTML
Parameters: {"utf8"=>"✓", "/users"=>{"name"=>"hello rails"}, "commit"=>"View Schedule"}
I also tried to use strong params as User.find_by(name: params[:users][:name]) which throws error "undefined method `[]' for nil:NilClass".
I think I am going wrong somewhere. Please correct me. Thank you for your time.
Issue lies here:
Parameters: {"utf8"=>"✓", "/users"=>{"name"=>"hello rails"}, "commit"=>"View Schedule"}
Do you see /users key in your params? It shoudn't be there. This indicates problem with your form:
<%= form_for users_path, :url => {:action => "testing", :name => :name}, :html => {:method => :get} do |f| %>
First argument is expected to be a string (which is then used as a name of form params) or ActiveModel object. In your case, it is string returned by users_path, which is just '/users'. It should be #testing
<%= form_for #testing, :url => {:action => "testing", :name => :name}, :html => {:method => :get} do |f| %>
That will fix your current issue, you will get another shortly after that, which should go into a separate question.
You'll be best using the following:
#app/views/users/testing.html.erb
<%= #user.try(:name) %>
<%= form_tag users_testing_path, method: :get do |f| %>
<%= f.text_field :name %>
<%= f.submit "View Schedule" %>
<% end %>
This will allow:
#app/controllers/users_controller.rb
class UsersController < ApplicationController
def testing
#user = User.find_by name: params[:name]
end
end
You could improve your routes file too:
#config/routes.rb
resources :users, path: "", only: :index, path_names: { index: "index" } do #-> url.com/index
get :testing, on: :collection #-> url.com/testing
end
This article explains how to let users make other users in devise. I've got it almost figured out, thanks to the answer provided in that article by Adam Waselnuk except I can't figure out how to create the same result with namespaced controllers and views. I get one of two errors, depending on how my routes.rb is set up... either Routing Error, No route matches [PUT] "/users/new" or
My code is below.
routes.rb
Rails.application.routes.draw do
namespace :admin do
resources :users, except: :create
end
post 'users/new' => 'admin/users#new', as: :create_user
resources :shows
resources :posts
resources :global_posts
devise_for :users, controllers: {
registrations: "users/registrations",
}
root 'pages#home'
get 'pages/home'
get 'admin' => 'admin#index', as: :admin
end
admin/users_controller.rb
class Admin::UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
# GET /admin/users
# GET /admin/users.json
def index
#all_users = User.all
end
def show
end
# GET /admin/users/new
def new
#user = User.new
end
# GET /admin/users/1/edit
def edit
end
# POST /admin/users
# POST /admin/users.json
def create
#user = User.new(user_params)
end
# PATCH/PUT /admin/users/1
# PATCH/PUT /admin/users/1.json
def update
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { render :show, status: :ok, location: #user }
else
format.html { render :edit }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
# DELETE /admin/users/1
# DELETE /admin/users/1.json
def destroy
#user.destroy
respond_to do |format|
format.html { redirect_to root_path, notice: 'User was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
#user = User.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit(
:name, :email, :password, :password_confirmation
)
end
end
And finally, the admin/users/new.html.erb
<h2>New User</h2>
<%= form_for #user, url: create_user_path, html: { method: :put } do |f| %>
<div>
<%= f.label :name %><br />
<%= f.text_field :name, autofocus: true %>
</div>
<div>
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true %>
</div>
<div>
<%= f.label :password %>
<%= f.password_field :password, autocomplete: "off" %>
</div>
<div>
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "off" %>
</div>
<div>
<%= f.submit "Create New User" %>
</div>
<% end %>
Your route should be
post 'users' => 'admin/users#create', as: :create_user
Then you can create the user in
def create
#user = User.new(user_params)
#user.save
end
I'm building a marketplace app where sellers can list items to sell. I use devise for authentication and also have user routes to collect user inputs after they sign up.
I'm trying to set this form up to collect two seller profile inputs from users and save it in the user model. But the controller does not read the user I get an error param not found: user error.
my routes: the match line was added for the profile form I'm posting about.
devise_for :users
resources :users, only: [:update, :edit]
match "/users/:id/sellerprofile", to: "users#sellerprofile", via: [:get, :put], as: :sellerprofile
my user controller method: note that I'm using the update process for a different form so I don't think I can use it for this form.
def sellerprofile
#user = User.find(params[:id])
# if params[:user]
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to root_url, notice: 'Your profile was successfully updated.' }
else
format.html { render action: 'edit' }
end
end
#end
end
def update
#user.attributes = user_params
Stripe.api_key = ENV["STRIPE_API_KEY"]
token = params[:stripeToken]
recipient = Stripe::Recipient.create(
:name => user_params["bankaccname"], #if i want to save to db, use this: #user.bankaccname. not saving it
:type => "individual", #gives you only one set of error messages. i don't need the respond
:bank_account => token #to block either i think. eg. when i enter firstname only, i get two error
) #msgs
#user.recipient = recipient.id
respond_to do |format|
if #user.save
format.html { redirect_to edit_user_url, notice: 'Your account was successfully updated.' }
else
format.html { render action: 'edit' }
end
end
end
def user_params
params.require(:user).permit(:bankaccname, :profileimage, :profilestory)
end
sellerprofile.html.erb
<%= form_for #user, url: sellerprofile_path(#user), html: { method: :put, :multipart => true } do |f| %>
<div class="form-group">
<%= f.label :your_story %><i> (required)</i>
<%= f.text_area :profilestory, class:"form-control" %>
</div>
<div class="form-group">
<%= f.label :profile_image %><i> (required)</i>
<%= f.file_field :profileimage, class:"form-control" %>
</div>
<div class="form-group">
<%= f.submit class:"btn btn-primary" %>
</div>
<% end %>
user.rb:
has_attached_file :profileimage,
:styles => { :profile => "200x200", :p_thumb => "100x100>" },
:default_url => ""
validates_attachment_content_type :profileimage, :content_type => /\Aimage\/.*\Z/
Routes
First things first:
#config/routes.rb
devise_for :users
resources :users, only: [:update, :edit] do
match :sellerprofile, via: [:get, :put], as: :sellerprofile
end
This is a much cleaner way to handle the route.
--
Controller
Next, you need to handle the data-processing capability of the controller. To do this, you need to make sure you're sending the right params (which I believe you are. If you aren't it will be in the url) of the form:
<%= form_for #user, url: sellerprofile_path, method: :put, html: { multipart: true } do |f| %>
You may wish to observe this routing structure inside Rails:
This is the standard routing structure inside Rails (called from the resources directive). This means that if you want to use a route equivalent to update, you will likely be able to omit the #user variable from it (although I've not tested this)
-
In order to make this work as correctly as you need, you need to ensure you're passing the parameters as follows:
params => {
"user" => {
"id" => "1",
"other" => "value",
}
This will be handled by the form_for method. When you call the user_params in your controller, the params.require block looks out for the user param; the permit block looking for the individual values contained within it.
This will allow you to do the following:
#app/controllers/users_controller.rb
def UsersController < ApplicationController
def sellerprofile
#user = User.find params[:id]
#user.update user_params
end
private
def user_params
params.require(:user).permit(:x, :y, :z)
end
end
This should work as you need
--
Update
Personally, I don't see the difference between what you're doing here, and with the update method?
If you want to use Stripe details in the update method, why not just have a separate method for the stripe processing, and call it if you have certain params set:
#app/controllers/users_controller.rb
class UsersController < ApplicationController
def update
#user.attributes = user_params
Stripe.api_key = ENV["STRIPE_API_KEY"]
token = params[:stripeToken]
if token. present?
recipient = stripe
#user.recipient = recipient.id
end
respond_to do |format|
if #user.update user_params
format.html { redirect_to edit_user_url, notice: 'Your account was successfully updated.' }
else
format.html { render action: 'edit' }
end
end
end
end
private
def stripe
return Stripe::Recipient.create(
:name => user_params["bankaccname"], #if i want to save to db, use this: #user.bankaccname. not saving it
:type => "individual", #gives you only one set of error messages. i don't need the respond
:bank_account => token #to block either i think. eg. when i enter firstname only, i get two error
)
end
def user_params
params.require(:user).permit(:x, :y, :z)
end
end
With my set up I have 2 types of Devise users Admins and Users I would like to be able to have the admins manage the users.
I have found some tutorials about this but they approach the problem from the perspective of a single User model with roles.
So far I've gotten to the point where, when I'm logged in as an admin, I can list the users, destroy the users and create new users, however, when I try to edit a user I get a blank form (as opposed to one that's populated by user information)
Any advice would be appreciated.
Below are the relevant files. Let me know if you need to see anything else.
/config/routes.rb
TestApp::Application.routes.draw do
devise_for :admins
devise_for :users
root to: 'pages#home'
# Admin Routes
if Rails.env.production?
devise_for :admins, :skip => [:registrations]
else
devise_for :admins
end
namespace :admins do
resources :users
end
authenticated :admin do
# For production because of skip registrations
get 'admins/edit' => 'devise/registrations#edit', as: :edit_admin_registration
put 'admins' => 'devise/registrations#update', as: :admin_registration
get 'admins/dashboard' => 'admins#dashboard', as: :admin_dashboard
devise_scope :admin do
get 'admins/list' => 'admins/users#index', as: :manage_users
get 'admins/users/new' => 'admins/users#new', as: :new_admins_user
get 'admins/users/:id/edit' => 'admins/users#edit', as: :edit_admins_user
post 'admins/users' => 'admins/users#create', as: :users
delete 'admins/users/:id' => 'admins/users#destroy', as: :destroy_admins_user
end
# Manage Content Routes
get '/pages/manage' => 'pages#manage', as: :manage_pages
get '/products/manage' => 'products#manage', as: :manage_products
end
authenticated :user, :admin do
get '/products' => 'products#index'
get '/pages/4' => 'products#index'
get '/gallery' => 'products#index'
end
unauthenticated do
devise_scope :users do
get '/pages/4' => 'devise/registrations#new'
get '/gallery' => 'devise/registrations#new'
end
end
resources :pages
resources :products
end
/controllers/admins_controller.rb
class AdminsController < ApplicationController
load_and_authorize_resource
def dashboard
render "admins/dashboard"
end
def index
respond_to do |format|
format.html
end
end
def destroy
#admin.destroy
redirect_to manage_admins_path
end
end
/controllers/admins/users_controller.rb
class Admins::UsersController < ApplicationController
load_and_authorize_resource
def index
#users = User.all
respond_to do |format|
format.html
end
end
def new
#resource = User.new
respond_to do |format|
format.html
end
end
def edit
#user = User.find(params[:id])
end
def create
#user = User.new(params[:user])
respond_to do |format|
if #user.save
format.html { redirect_to manage_users_path, notice: 'User was successfully created.' }
else
format.html { render new_admin_user_path }
end
end
end
def update
#user = User.find(params[:id])
if params[:user][:password].blank?
params[:user].delete(:password)
params[:user].delete(:password_confirmation)
end
respond_to do |format|
if #user.update_attributes(params[:user])
format.html { redirect_to manage_users_path, notice: 'User was successfully updated.' }
else
format.html { render action: "edit" }
end
end
end
def destroy
#user = User.find(params[:id])
#user.destroy
redirect_to manage_users_path
end
# private
# def check_permissions
# authorize! :create, resource
# end
end
/views/admins/users/edit.html.haml
.input-form
%h2
Edit #{resource_name.to_s.humanize}
= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f|
= devise_error_messages!
%fieldset{id: "edit-your-account"}
.field.required
= f.label :first_name
= f.text_field :first_name, :autofocus => true
.field.required
= f.label :last_name
= f.text_field :last_name
.field.required
= f.label :company
= f.text_field :company
.field.required
= f.label :phone
= f.text_field :phone
.field.required
= f.label :email
= f.email_field :email
.field.required
= f.label :password
= f.password_field :password
%span.instructions
(leave blank if you don't want to change it)
- if devise_mapping.confirmable? && resource.pending_reconfirmation?
%br
Currently waiting confirmation for:
= resource.unconfirmed_email
.field.required
= f.label :password_confirmation
= f.password_field :password_confirmation
.field.required
= f.label :current_password
= f.password_field :current_password
%span.instructions
(we need your current password to confirm your changes)
.field
= f.submit "Update"
= link_to "Back", :back
/helpers/admins_helper.rb
module AdminsHelper
# Devise helpers for Admin::UsersController
def resource_name
:user
end
def resource
#resource ||= User.new
end
def devise_mapping
#devise_mapping ||= Devise.mappings[:user]
end
end
Looks like renaming your instance variable should do the trick. Your edit template passes the object resource to the form but the object you've loaded from the database is set to #user:
# Admins::UsersController
def edit
#user = User.find(params[:id])
end
# AdminsHelper
def resource
#resource ||= User.new
end
You could pass this instance variable to the form_for instead, or rename #user as #resource so the helper method will return the proper instance.