In my rails app I would like to have two patch methods for updating the users profile.
First, I will have an 'account_settings' GET request, which will use the standard edit/update REST action to update certain parameters. Then, I would like to have an additional 'edit_profile' and 'update_profile' actions to get a page that will allow the user to update different user attributes. Here's how it looks in my users_controller.rb for a better idea:
#For account settings page
#This is for the account settings page
#(only changing email and password)
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
respond_to do |format|
if #user.update_attributes(account_settings_params)
flash.now[:success] = "Your settings have been successfully updated."
format.html {redirect_to #user}
else
format.html {redirect_to edit_user_path}
flash[:error] = "Please be sure to fill out your email, password, and password confirmation."
end
end
end
def edit_profile
#user = User.find(params[:id])
end
#For update profile
def update_profile
#user = User.find(params[:id])
respond_to do |format|
if #user.update_attributes(user_profile_params)
flash.now[:success] = "Your profile has been updated."
format.html {redirect_to #user}
else
format.html {redirect_to edit_profile_user_path}
flash[:error] = "Please be sure to fill out all the required profile form fields."
end
end
end
private
def account_settings_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
def user_profile_params
params.require(:user).permit(:name, :about_me, :location, :image_url)
end
Selection from my current routes.rb :
#Account Settings Just for Email and Password
get 'account_settings' => 'users#edit'
patch 'settings' => 'users#update'
resources :users do
member do
get :edit_profile
put :update_profile
end
end
Results of rake routes:
edit_profile_user GET /users/:id/edit_profile(.:format) users#edit_profile
update_profile_user PATCH /users/:id/update_profile(.:format) users#update_profile
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PATCH /users/:id(.:format) users#update
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
My navbar partial:
-if logged_in?
-# Logged in links
%li
=link_to 'Logout', logout_path, method: :delete
%li
=link_to 'Account Settings',edit_user_path(#current_user)
%li
=link_to 'Edit My Profile', edit_profile_user_path(#current_user)
%li
=link_to 'View My Profile', user_path(#current_user)
%li
=link_to 'Members', users_path
On the edit_profile page, my form looks like this:
=form_for #user, path: update_profile_user_path(#user) do |f|
With my current implementation, visiting the edit_profile page and posting the form will lead back to the regular edit page with my rails server saying that the parameters were unpermitted. However, as you can see in my update_profile method in my controller, the controller method for update_profile accepts user_profile_params rather than the account_settings_params . Any insight onto why it might be doing this?
A few notes:
You don't need render "edit_profile" because that is done by default
You don't need to overwrite the edit route
I'd strongly suggest actually having a separate Profile controller, instead of trying to hack it in as extra actions on user.
That being said, the routes look like
resources :users do
member do
get :edit_profile
patch :update_profile
end
end
then your link would be to users/1/edit_profile (link_to "edit", edit_profile_path(#user)) and the form would be to <%= form_for #user, path: update_user_path(#user) %>
To add RESTful routes to the current resources you can use collection or members based on the requirement.
collection is used as a non membered resources like index action which gives a collection of objects where as show action needs an object to show. Hence member is used to get the action of a single object.
Here you can use
resources users do
resources member do
get :edit_profile
put :update_profile
end
end
You can also use
resources :users do
get :edit_profile, on: :member
put :update_profile, on: :member
end
Related
No route matches {:action=>"show", :controller=>"users"}
Working with a condition current_page in rails with this format. Trying to not render a navbar within users/show path, but it should be visible in the rest of site. One thing to note is that the users/show URL has been configured in routes.rb to not show '/users/' folder in the URL, so it looks like 'mysite.com/username'
<% if current_page?(controller: 'users', action: 'show') %>
no navbar
<% else %>
<%= render partial: "shared/navbar" %>
<% end %>
The first condition works fine, however when I reach a page that should match the 'else' condition, for instance my root_path, I get this error:
ActionController::UrlGenerationError in Dashboard#show
Showing /Users/javier/Desktop/rails-apps/testtradus3/app/views/shared/_navbar.html.erb where line #1 raised:
No route matches {:action=>"show", :controller=>"users"}
My route.rb looks like this
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
Rails.application.routes.draw do
...
# This removes '/users/' from user/show URI
resources :users, path: '', :only => [:show]
# User account
devise_for :users,
controllers: {
omniauth_callbacks: "users/omniauth_callbacks",
registrations: "users/registrations",
sessions: "users/sessions"
}
devise_scope :user do
get "session/otp", to: "sessions#otp"
end
resources :users do
resources :builduser, controller: 'users/builduser'
end
...
end
This returns this rails routes:
users GET /users(.:format) users#index
POST /users(.:format) users#create
I have tried removing the custom path in routes.rb, so something like resources :users and that returns these routes
users GET /users(.:format) users#index
POST /users(.:format) users#create
GET /users(.:format) users#index
POST /users(.:format) users#create
GET /users/new(.:format) users#new
GET /users/:id/edit(.:format) users#edit
GET /users/:id(.:format) users#show
My UsersController.rb
class UsersController < ApplicationController
def index
#users = User.all
end
def show
#user = User.friendly.find(params[:id])
#order = Order.new
end
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
# format.html { redirect_to #order, notice: "Order was successfully created." }
# Added this one below:
format.html { redirect_to user_builduser_index_path(#user)}
format.json { render :show, status: :created, location: #user }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
..
end
Going off some of the examples listed in the link below, you could try:
current_page?(users_path(current_user))
# or
current_page?(users_path(#user))
# or even
current_page?("/#{#user&.id}")
An alternative could be to set a before_action in your ApplicationController to set an instance variable:
before_action :display_nav_bar # this would apply to all routes unless overridden like below in the UsersController
def display_nav_bar
#display_nav_bar = true
end
# and in your UsersController:
def show
#display_nav_bar = false
# other stuff
#user = User.find(params[:id)
end
then
<% if #display_nav_bar %>
<%= render partial: "shared/navbar" %>
<% else %>
no navbar
<% end %>
You could also look into using layouts for different controllers and control rendering the nav bar that way.
current_page? source
I'm trying to update a record with an admin user with a "validate" button that changes the record's status from pending to confirmed. I've created a form, and the route to do so, however the controller is giving me trouble, I'm not sure what to code for it to update the specific record i click validate for.
Hours controller
def index
#allhours = HourLog.where(status:'Pending')
end
def update
#hour = HourLog.where(status:'Pending')
#hour.update(#hour.id, :status)
end
Hours/index.html.erb
<td><%=form_for(:hour_log, method: :put) do |f|%>
<%= f.hidden_field :status, :value => 'Confirmed' %>
<%= f.submit 'Validate'%>
<%end%>
</td>
Any help would be fantastic, thanks!
error:
NoMethodError in HoursController#update
undefined method `id' for #
I know something is wrond with the (#hour.id) section of the controller in the update def, but I don't know what to replace it with
edit: rake routes
Prefix Verb URI Pattern Controller#Action
root GET / welcome#index
signup GET /signup(.:format) users#new
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PATCH /users/:id(.:format) users#update
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
GET /users(.:format) users#index
PUT /users(.:format) users#update
login GET /login(.:format) sessions#new
POST /login(.:format) sessions#create
logout DELETE /logout(.:format) sessions#destroy
dashboard GET /dashboard(.:format) hours#new
dashboard_hours GET /dashboard/hours(.:format) hours#index
hours PUT /hours(.:format) hours#update
GET /hours(.:format) hours#index
POST /hours(.:format) hours#create
new_hour GET /hours/new(.:format) hours#new
edit_hour GET /hours/:id/edit(.:format) hours#edit
hour GET /hours/:id(.:format) hours#show
PATCH /hours/:id(.:format) hours#update
PUT /hours/:id(.:format) hours#update
DELETE /hours/:id(.:format) hours#destroy
hours Controller
class HoursController < ApplicationController
before_action :require_user
before_action :require_admin, only: [:index]
def index
#allhours = HourLog.where(status:'pending')
end
def new
#hour = current_user.hour_logs.new
#entry = current_user.hour_logs.all
end
def edit
flash[:warning] = "Hash: #{params}"
end
def create
#hour = HourLog.new(hour_params)
#hour.user_id = current_user.id if current_user
#hour.status = 'pending'
if #hour.save
redirect_to '/dashboard'
end
end
private
def hour_params
params.require(:hour_log).permit(:assignment, :hours, :supervisor, :date)
end
def update_hour_params
params.require(:hour_log).permit(:status)
end
end
HourLog method
class HourLog < ActiveRecord::Base
belongs_to :user
end
I see this has an answer accepted, but I'd like to propose an alternative solution for anyone who may come along and find this question. Assumptions: The base page is an index with all pending items and the click is a simple state change. (A state toggle could be easily added to this solution.)
(I'm using the naming from the question.)
Using link_to as a message
A state change from 'Pending' to 'Confirmed' for a flag could be implemented by using the link_to to trigger the controller action to update this flag. Hash params may be passed via a link_to enabling simplified logic in the controller.
VIEW: Index.html.erb
<% #allhours.each do |hour| %>
<ul>
<li><%= hour.textdescriptionvar %> is an item pending approval.
<%= link_to(" Click to approve", edit_hour_path(hour, :status => "Confirmed"))
</li>
</ul>
<% end %>
CONTROLLER: HourLogsController
def edit
flash[:info] = "Hash: #{params}"
#hour = HourLog.find(params[:id])
end
def update
#hour = HourLog.find(params[:id])
#hour.update_attributes(hours_params)
if #hour.save
redirect_to #hour, :notice => "Successfully changed stat."
else
render 'index' # Any action here, index for example
end
end
private
def hours_params
params.require(:hour_log).permit(:status)
end
I would recommend a data: confirm message be added to the link_to,but I wanted to mimic the poster's situation as posted.
Rails spec for link_to here.
#hour = HourLog.where(status:'Pending') will return an ActiveRecord Relation, not a single record.
Try this:
#hour = HourLog.find_by(status: 'pending')
#hour.update_attribute :status, params[:hour_log][:status]
This method finds the first record with status ="pending" and updates that instead of the specific record.
You're going to want #hour.update params[:hour_log] instead because update_attribute doesn't run validations. and keep data in your database lowercase unless it's something written by a user. this link is useful http://www.davidverhasselt.com/set-attributes-in-activerecord/
My rails app is adding a dot/period "." into the route when trying to browse to a devise gem user's profile.
routes.rb:
devise_for :users, :controllers => { :registrations => "registrations" }
(Basically using routes to allow for some custom functions - it should be still using the default devise routing paths.
Rake routes output:
user_registration POST /users(.:format) registrations#create
new_user_registration GET /users/sign_up(.:format) registrations#new
edit_user_registration GET /users/edit(.:format) registrations#edit
Link in view:
<%= link_to 'Edit', edit_user_registration_path(user) %>
Where user is defined in a .each loop.
Outputted Link in URL looks like:
http://localhost:3000/users/edit.2
My goal is to have a link to edit any user's profile (permissions controlled outside these items of course).
I "think" my problem is the routes.rb and not specifying the path correctly there. I think it should be something that would have a rake routes that includes an :id parameter.
Thanks for the help!
That's normal routes's devise, registrations#edit is for edit current signed-in user, If you want to CRUD interface for users (I think you want to editing a user), you can add another controller (e.g users_controller.rb) and make some action , example for controller :
def edit
#user = User.find(params[:id])
end
def update
if params[:user][:password].blank?
params[:user].delete(:password)
params[:user].delete(:password_confirmation)
end
#user = User.find(params[:id])
respond_to do |format|
if #user.update_attributes(params[:user])
format.html { redirect_to some_path, notice: 'Updated.' }
format.json { head :no_content }
else
format.html {
flash[:alert] = "Something wrong"
render :action => :edituser
}
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
On your routes.rb you can following like this
scope '/user' do
match '/edit/:id' => 'users#edit', :as => :edit_user
put '/update/:id' => 'users#update', :as => :update_user
end
And links on each section looks like :
<%= link_to 'Edit', edit_user_path(user) %>
references :
How To: Manage users through a CRUD interface
CRUD Devise Example
I think Devise's edit_user_registration_path uses current_user internally, so you shouldn't need the (user) in your link_to
I'm trying to add a Tenant user inside a Property#show view. I have a form there like the following:
<%= form_for(#property.tenants) do |f| %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :email %><br />
<%= f.text_field :email %>
</div>
<div class="select">
<%= f.select :type, [['Landlord'],['Tenant']] %>
</div>
<%= f.submit "Add", %>
<% end %>
The Tenant model is as follows:
class Tenant < User
belongs_to :property
def type
"Tenant"
end
end
and the Property model is as follows:
class Property < ActiveRecord::Base
attr_accessible :address, :bathrooms, :bedrooms, :postcode, :price
belongs_to :branch
belongs_to :user
has_many :ownerships
has_many :landlords, :through => :ownerships
has_many :tenants
end
When I click on the Add button i'm magically redirected to the root path (/).
I'm expecting it to add a new tenant for that specific property that I'm viewing in the show view but it just redirects me to the root path.
Feel free to ask for any clarifications
results of rake routes:
tenants_index GET /tenants/index(.:format) tenants#index
tenants_show GET /tenants/show(.:format) tenants#show
tenants_new GET /tenants/new(.:format) tenants#new
landlords_new GET /landlords/new(.:format) landlords#new
tenants_edit GET /tenants/edit(.:format) tenants#edit
tenants_create GET /tenants/create(.:format) tenants#create
tenants_update GET /tenants/update(.:format) tenants#update
tenants_destroy GET /tenants/destroy(.:format) tenants#destroy
properties GET /properties(.:format) properties#index
POST /properties(.:format) properties#create
new_property GET /properties/new(.:format) properties#new
edit_property GET /properties/:id/edit(.:format) properties#edit
property GET /properties/:id(.:format) properties#show
PUT /properties/:id(.:format) properties#update
DELETE /properties/:id(.:format) properties#destroy
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
companies GET /companies(.:format) companies#index
POST /companies(.:format) companies#create
new_company GET /companies/new(.:format) companies#new
edit_company GET /companies/:id/edit(.:format) companies#edit
company GET /companies/:id(.:format) companies#show
PUT /companies/:id(.:format) companies#update
DELETE /companies/:id(.:format) companies#destroy
branches GET /branches(.:format) branches#index
POST /branches(.:format) branches#create
new_branch GET /branches/new(.:format) branches#new
edit_branch GET /branches/:id/edit(.:format) branches#edit
branch GET /branches/:id(.:format) branches#show
PUT /branches/:id(.:format) branches#update
DELETE /branches/:id(.:format) branches#destroy
sessions POST /sessions(.:format) sessions#create
new_session GET /sessions/new(.:format) sessions#new
session DELETE /sessions/:id(.:format) sessions#destroy
agents GET /agents(.:format) users#index
POST /agents(.:format) users#create
new_agent GET /agents/new(.:format) users#new
edit_agent GET /agents/:id/edit(.:format) users#edit
agent GET /agents/:id(.:format) users#show
PUT /agents/:id(.:format) users#update
DELETE /agents/:id(.:format) users#destroy
landlords GET /landlords(.:format) users#index
POST /landlords(.:format) users#create
new_landlord GET /landlords/new(.:format) users#new
edit_landlord GET /landlords/:id/edit(.:format) users#edit
landlord GET /landlords/:id(.:format) users#show
PUT /landlords/:id(.:format) users#update
DELETE /landlords/:id(.:format) users#destroy
tenants GET /tenants(.:format) users#index
POST /tenants(.:format) users#create
new_tenant GET /tenants/new(.:format) users#new
edit_tenant GET /tenants/:id/edit(.:format) users#edit
tenant GET /tenants/:id(.:format) users#show
PUT /tenants/:id(.:format) users#update
DELETE /tenants/:id(.:format) users#destroy
root / static_pages#home
signup /signup(.:format) users#new
signin /signin(.:format) sessions#new
signout DELETE /signout(.:format) sessions#destroy
dashboard /dashboard(.:format) static_pages#dashboard
help /help(.:format) static_pages#help
about /about(.:format) static_pages#about
contact /contact(.:format) static_pages#contact
properties_controller.rb
class PropertiesController < ApplicationController
# GET /properties
# GET /properties.json
def index
#properties = Property.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #properties }
end
end
def show
#property = Property.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #property }
end
end
# GET /properties/new
# GET /properties/new.json
def new
# #property = Property.new
#property = current_user.properties.build if signed_in?
respond_to do |format|
format.html # new.html.erb
format.json { render json: #property }
end
logger.debug("hello from new")
end
# GET /properties/1/edit
def edit
#property = Property.find(params[:id])
end
# POST /properties
# POST /properties.json
def create
##property = Property.new(params[:property])
#property = current_user.branch.properties.build(params[:property]) if signed_in?
respond_to do |format|
#property.user_id = current_user.id
if #property.save
format.html { redirect_to #property, notice: 'Property was successfully created.' }
format.json { render json: #property, status: :created, location: #property }
else
format.html { render action: "new" }
format.json { render json: #property.errors, status: :unprocessable_entity }
end
end
end
end
I think there are several things that doesn't seem quite right.
Try this.
Add #tenant instance variable to the show method of Properties controller.
def show
...
#tenant = #property.tenants.build
end
And modify form_for line to this.
<%= form_for #tenant do |f| %>
In your properties_controller.rb,
def show
#tenant = Tenant.new
end
Your from should then be:
<%= form_for #tenant do |f| %>
You should also add a hidden field with the ID of the property for when you submit your form.
Then for your create action in properties_controller.rb
def create
tenant = Tenant.new(params[:tenant])
tenant.property_id = params[:property_id] #This is from that hidden field with your property_id and this also assumes you have a column in your tenant table for property_id
tenant.save
#the shorter way would just be tenant = Tenate.create(params[tenant]) but I want to emphasize the property_id since this attribute is paramount to your property-tenant relationship
end
This should effectively create a form for a new tenant and when you create the actual tenant record, you will save the property ID with the new tenant record.
On another note, I would suggest moving this to the Tenants controller per Rails convention and having the form under the new action since you are really creating a new tenant record.
I'm a rails beginner and have been several hours now trying to figure out why my "generate new password" link, which was supposed to execute my custom update_password action inside my users controller, and then flash the password back on the screen (later i plan to send this by e-mail or sms, but for now this would do) keeps executing the create method/action...
I understand I have two POST methods for the users index screen...but cannot understand (since i even placed it first on the routes file) why create is the one that keeps getting executed. I know it doesn't go to the update method, because i filled it with debugger logging messages, which do not show up anywhere. (and the logging is active since i see the index method logging message)
Here is what I'm doing :
routes.rb file extract :
match 'users?user_id=(:user_id)', to: "users#update_password", via: :post, as: "users_update_password"
resources :users
rake routes (controller users)
users_update_password POST /users?user_id=(:user_id)(.:format) users#update_password
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
signup /signup(.:format) users#new
users_controller.rb
def update_password
logger.info "Inside update_password"
logger.flush
password= generate_password()
logger.debug "Password is #{password}"
#user = User.find_by_id(params[:user_id])
logger.debug "user is #{#user}"
if #user.update_attributes(password:password, password_confirmation:password)
logger.info "inside the if"
flash[:notice] = "New password for user #{#user.name}: #{password}"
logger.debug "Flash is #{flash}"
redirect_to users_path
else
logger.debug "I am on else of update_password"
flash[:alert] = "Name: #{#user.name} password: #{password}"
render 'index'
end
end
def index
logger.info "Inside controller index method"
#users = User.paginate(page: params[:page])
end
_user.html.erb
<% if current_user.admin? && !current_user?(user) %>
| <%= link_to "delete", user, method: :delete, confirm: "Are you sure?" %>
| <%= link_to "Generate new password", users_update_password_path(:user_id=>user.id), method: :post, confirm: "Are you sure? This will reset current password" %>
<% end %>
Thank you for your help
It might simplify things if you make this a member of your resource routes?
resources :users do
member do
post 'update_password'
end
end
Then path helper probably looks like
link_to "Generate new password", update_password_user_path(user), method: :post
# POST. /users/:id/update_password
http://guides.rubyonrails.org/routing.html#adding-more-restful-actions
When you use HTTP POST, the data is not sent in the URL. Your route is looking for data in the URL, which is done via HTTP GET. So if you want to use a POST, make your route something like:
post "users/update_password" => "users#update_password", :as => "users_update_password"