I am trying to delete all task that is linked to logged in user but when I click on delete all button it shows the error
No route matches [POST] "/tasks/destroy_all"
task_controller.rb
class TaskController < ApplicationController
def all_destory
#user = current_user
#user.tasks.destroy_all
redirect_to user_tasks_path
end
end
route.rb
get '/tasks/destroy_all', to: 'task#all_destory', as: :destroy_all
HTML
<% #tasks.each do |task| %>
<%= task.daily_task %>
<%= task.date %>
<% end%>
<%= button_to "delete all", destroy_all_path %>
When destroying records you want to use the DELETE HTTP verb.
GET requests are saved in the browsers history and should not create, modify or destroy anything on the server.
Typically in Rails you just have a route to destroy a single record. But if DELETE /things/1 deltes a single resource then DELETE /things should logically destroy the entire collection:
get '/user/tasks', to: 'users/tasks#index', as: :user_tasks
delete '/user/tasks', to: 'users/tasks#destroy_all'
# app/controllers/users/tasks_controller.rb
module Users
class TasksController < ApplicationRecord
before_action :authenticate_user!
# display all the tasks belonging to the currently signed in user
# GET /user/tasks
def index
#tasks = current_user.tasks
end
# destroy all the tasks belonging to the currently signed in user
# DELETE /user/tasks
def destroy_all
#tasks = current_user.tasks
#tasks.destroy_all
redirect_to action: :index
end
private
# You don't need this if your using Devise
def authenticate_user!
unless current_user
redirect_to '/path/to/your/login',
notice: 'Please sign in before continuing'
end
end
end
end
<%= button_to "Delete all", user_tasks_path, method: :delete %>
Your HTTP verb and your route must match. Currently your button is using POST, but your route accepts GET. You could change them both to POST.
post '/tasks/destroy_all', to: 'task#all_destory', as: :destroy_all
This fixes the problem in the question, but it's not ideal. As #max points out, DELETE would be more communicative of what clicking the button does– delete resources.
DELETE documentation
Related
I am using devise for user management so it let's user sign up with default email and password fields.
I added new fields/columns into the user model say username, designation and company.
So I have a profile view say with route '/users/1' and a link_to helper which would allow me to edit and update my user info.
By default i can only use users/edit route to edit my user info. How can i manage a new or separate edit and update option with different route say '/users/1/edit' from my profile view.
I read some posts before this but didn't help me. If anyone could outline things i should do. Thanks for reading :))
Edit:
routes file
root 'public#index'
devise_for :users
resources :users do
put 'users/:id/edit', to: 'users#edit'
end
user controller
class UsersController < ApplicationController
before_action :authenticate_user!
after_action :verify_authorized
before_action :set_user, only: %i[ show edit update ]
def index
#users = User.all
authorize User
end
def show
authorize #user
end
def edit
if current_user == #user
#user.update()
end
end
def update
authorize #user
if #user.update(secure_params)
redirect_to users_path, :notice => "User updated."
else
render 'edit'
end
end
private
def secure_params
params.require(:user).permit(:designation, :company,
:username)
end
def set_user
#user = User.find(params[:id])
end
end
In my view to go to edit:
<% if current_user.id == #user.id %>
<%= link_to 'Edit My profile', edit_user_path(#user), method: :edit,
class:"btn btn-primary" %>
<% end %>
If you really want to have a route user/:id/edit and not use the Devise default users/edit route(which edits the currently logged-in user). You can do the following:
Let's assume you have a users controller(if you don't have one, create one) and add an edit action to it which will handle the editing logic:
class UsersController < ApplicationController
# other code
def edit
user = User.find_by(id: params[:id]) # this id will be passed through the route
# Now here you need some authorization logic to prevent users from updating others.
# If you use CanCanCan, Pundit or any other authorization gem then write
# this logic there
if current_user == user
user.update() # do your update logic here with params you have
# render some json or whatever you want
else
# render some error messages in format you are using
end
end
end
This is the controller logic, now in your routes.rb file you need to register this route:
put 'user/:id/edit', to: 'users#edit'
This will edit the user with ID specified at :id.
Note again: This is not the approach I would take, I would rather just use the users/edit route and update the currently logged in user, but you wanted an example of this so do as you will
I have an app where users can ask questions and bookmark certain questions. I'm done with the users, questions, and answers, so I've added a BookmarkController & Bookmarks model. At first, I considered using associations, but my app has a few associations already so I'm (or I've attempted at) using query parameters such as user_id and question_id to fetch bookmarks.
The structure is a bit like StackOverflow. A user navigates to a single question view and bookmarks it on that page. This creates a new bookmark model containing the user_id of current_user and the question_id. The user can go to his profile to view all the questions he bookmarked, fetched using his user_id. (Answers cannot be bookmarked. Only questions.)
I've been getting a 'param is missing or the value is empty: bookmark' error, although I have followed similar steps I did for my QuestionsController. It would be great if someone could help me out in identifying what's wrong/bad about my code!
rake routes (first part omitted)
bookmark_question PUT /questions/:id/bookmark(.:format) questions#bookmark
questions GET /questions(.:format) questions#index
POST /questions(.:format) questions#create
new_question GET /questions/new(.:format) questions#new
edit_question GET /questions/:id/edit(.:format) questions#edit
question GET /questions/:id(.:format) questions#show
PATCH /questions/:id(.:format) questions#update
PUT /questions/:id(.:format) questions#update
DELETE /questions/:id(.:format) questions#destroy
route.rb (excerpt)
# Questions
get '/questions/:id' => 'bookmarks#create'
show.html.erb (questions#show)
<% if current_user %>
<%= link_to "Bookmark", :controller => 'bookmarks', :action => 'create' %>
<% end %>
BookmarksController
class BookmarksController < ApplicationController
def new
#bookmark = Bookmark.new
end
def create
#question = Question.find(params[:id]) # when I delete this line, I get a new error - "undefined local variable 'params'"
#bookmark = Bookmark.new(bookmark_params)
#bookmark.user_id = current_user.id
#bookmark.question_id = #question.id
#bookmark.save
redirect_to #question
end
def destroy
end
private
def bookmark_params
params.require(:bookmark).permit(:user_id, :question_id)
end
end
Bookmark model
class Bookmark < ApplicationRecord
validates :user_id, presence: true
validates :question_id, presence: true
end
QuestionsController
(at the moment, contains no reference to Bookmarks. I thought so because I did the routing, but this might be where I'm going wrong)
class QuestionsController < ApplicationController
def index
#questions = Question.all
end
def show
#question = Question.find(params[:id])
#answers = Answer.all
# Delete only appears when no answers
#deletable = (current_user== User.find(#question.user_id)) && (#question.answers.all.size==0)
end
def new
#question = Question.new
end
def create
if logged_in?
#question = Question.new(question_params)
#question.user_id = current_user.id
#question.save
redirect_to #question
else
redirect_to login_path
end
end
def destroy
#question = Question.find(params[:id])
#question.destroy
redirect_to root_path
end
private
def question_params
params.require(:question).permit(:picture_url, :country, :educational_level, :topic)
end
end
profile index.html.erb (just for ref)
<% if (#bookmarks.count == 0) %>
///
<% else %>
<%= #bookmarks.each do |bookmark| %>
<!-- Show bookmark content here like Question.find(bookmark.question_id) etc -->
<% end %>
<% end %>
I have looked a the previous qns that have the same error as me. But they were all using associations. I hope to not use associations as the bookmark model only needs to keep a record of the user id and qn id.
UPDATE
So, referring to the answers given, I updated my erb to:
<% if logged_in? %>
<%= link_to "Bookmark", :controller => 'bookmarks', :action => 'create', bookmark: {user_id: current_user.id, question_id: #question.id} %>
<% end %>
hence specifying the controller and action (and the params) that need to be directed. But rails sends an error:
No route matches {:action=>"create", :bookmark=>{:user_id=>2, :question_id=>4}, :controller=>"bookmarks", :id=>"4"}
So I assume it was a routing problem. As Pavan suggested, I did consider nesting my resources, but the nesting is already one level deep, as such:
resources :questions do
resources :answers
end
And I reckon doing something like:
resources :questions do
resources :bookmarks # or resources :bookmarks, only: create
resources :answers
end
won't work. (And it didn't :( )
I'm not so sure how to get this routing problem fixed (tried Googling). Thanks.
param is missing or the value is empty: bookmark
The reason for the error is bookmark_params expects a :bookmark key to be present in the params hash, which in your case is missing since you are not passing any.
Change link_to like below:
<% if current_user %>
<%= link_to "Bookmark", :controller => 'bookmarks', :action => 'create', bookmark: {user_id: current_user.id, question_id: #question.id} %>
<% end %>
Also, the route get '/questions/:id' => 'bookmarks#create' isn't right and would conflict with this route question GET /questions/:id(.:format) questions#show. I would instead recommend building nested routes
resources :users do
resources :questions do
resources :bookmarks, only: [:create]
end
end
Update:
Along with the above, you should change #question = Question.find(params[:id]) to #question = Question.find(params[:bookmark][:question_id])
'param is missing or the value is empty: bookmark, this error means that, there is no bookmark key present in your params object, but you defined your bookmark_params to have one:
def bookmark_params
params.require(:bookmark).permit(:user_id, :question_id)
end
That's why it's throwing the above error message.
You should make sure you send the user_id and question_id key/value pairs under the bookmark key. Something like this:
bookmark: { user_id: 1, question_id: 2}.
So, your code should look something like this (adding the bookmark to params):
<%= link_to "Bookmark", :controller => 'bookmarks', :action => 'create', bookmark: {user_id: current_user.id, question_id: #question.id} %>
I want a simple system where users have three types (user, admin and medic) and the ones tagged Admin can "activate" other users' accounts so they can access certain privileges. I tried doing this with a simple button but I haven't found a way to do so.
The button for the code is:
<%= button_to "Change user Type to Medic", :method=> "activate_medic" %>
My activation method is as so:
def activate_medic
#user = User.find(params[:id])
#user.activated = true
if #user.save
flash[:info] = "Success"
end
end
And there's a post 'users/activate_medic' in my routes.rb file.
However, pressing the button brings up:
ActionController::RoutingError (No route matches [POST] "/users/1"):
If I'm trying to edit user 1.
Not exactly what I proposed in the original question, but:
I allowed the Admin users to completely edit a given user's info and by proxy their user type and permissions:
Users controller:
before_action :correct_user, only: [:edit, :update]
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless current_user?(#user) || current_user.type == "Admin"
end
However, to avoid any user from just accessing their edit page and giving themselves admin rights, I edited the form to only allow Admins to see the field that edits their type:
<% if #user.type == "Admin" %>
<%= f.label :type, "User Type:" %>
<%= f.text_field :type, class: 'form-control' %>
<% end %>
The method option in link_to is meant to specify an HTTP verb, not the name of a custom method. Based on what you've provided, I would approach the problem like this:
button_to "Change user Type to Medic", activate_user_path(#user), method: :patch
And a dedicated controller:
class ActivateUserController
def update
#user = User.find(params[:id])
if #user.activate
flash[:info] = "Success"
end
redirect_to #user
end
end
And a route:
resources :users do
patch :activate, to: 'activate_user#update', as: :activate_user
end
And finally, move user behavior into the User model:
class User
def activate
self.update_attribute!(:active, true)
end
end
I created the model User and the model Profile. On my homepage I have a link in the dropmenu navigation bar that links to Edit Profile. The problem I face is "No route matches {:action=>"edit", :controller=>"profiles", :id=>nil} missing required keys: [:id]".
The route for edit page is "edit_profile_path" with verb GET and URI pattern "/profiles/:id/edit(.:format)". I am having a hard time getting the "id" inserted. Below is the code that I have on my app.
In model Profile file I have:
class Profile < ActiveRecord::Base
belongs_to :user, dependent: :destroy
end
In model User file I have:
class User < ActiveRecord::Base
has_one :profile
end
The profile has many attributes, but one of them is "user_id" which is an integer that is equal to the User's id. So User #5 with id#5 is the owner of Profile#5.
Here is the code that I have in the View file:
<li><%= link_to "Edit Profile", edit_profile_path(#profile) %></li>
With regards to the code directly above, I have tried inserting different codes inside the parenthesis, from #profile.id, #profile, #user.id, and #user. But it has not worked.
I created a profiles controller and I think (but I am not certain) that my problem is coming from the profiles_controller file. Here is the code that I have:
class ProfilesController < ApplicationController
before_action :authenticate_user!
before_action :set_profile, only: [:edit, :update]
def edit
end
def new
#profile = Profile.new
end
def create
#profile = Profile.new(profile_params)
#profile.user_id = current_user.id
if #profile.save
redirect_to welcome_path
else
render 'new'
end
end
def update
#profile.update(profile_params)
redirect_to welcome_path
end
private
def set_profile
#profile = Profile.find(params[:id])
end
end
You are getting this error because in your view, your #profile in nil.
So, you have to get the current_profile in your view so that you can go to the edit page of that profile.
If you already have access to your current_user helper method, then, in your view, you can simply do:
<li><%= link_to "Edit Profile", edit_profile_path(current_user.profile) %></li>
A few things to note (which may be the key to solving your problem).
You are having a 1 to 1 relationship, and the user can access his profile only when he is logged in. Since you already have a (presumably properly working) current_user method, use it all the time.
def new
current_user.build_profile
end
def create
current_user.build_profile(profile_params)
#etc
end
It's also a logical way to get the user's profile
private
def set_profile
#profile = current_user.profile
end
In your view:
<%= link_to edit_profile_path(current_user.profile) %>
I think this makes much more sense in your code and is much more readable. Additionally, I think such approach will save you a lot of errors such as the one you're encountering now.
Have you tried?
edit_profile_path(id: #profile.id)
Also did you put this route in your routes file?
So I am in the process of setting up a forum and everything is setup/working well except for my replies are not appearing on the thread "show" page. After checking the rails console, I see they are saving but the user_id and discussion_id are no. The user_id is always nil and the discussion_id is always 0. The discussion threads were easier to setup but with having these replies, I obviously seem to be having an issue. Here are my snippets of code:
class PostsController
# ...
before_filter :authenticate_user!
before_filter :set_discussion, only: [:new, :create, :destroy]
def create
#post = #discussion.post.new(create_params) do |post|
post.user = current_user
end
if #post.save
redirect_to #discussion, notice: "It has been posted!"
else
render :new
end
end
def destroy
#post = #discussion.posts.find(params[:id])
#post.destroy
flash.notice = "Deleted"
redirect_to discussion_path(#discussion)
end
private
def create_params
params.require(:post).permit(:reply)
end
def set_discussion
#discussion = Discussion.friendly.find(params[:id])
end
end
class DiscussionsController
def show
#discussion = Discussion.friendly.find(params[:id])
#post = Post.new
render :layout => 'discussion'
end
end
Partial rendered to reply:
<h2>Reply</h2>
<%= form_for [ #discussion, #post ] do |f| %>
<p>
<%= f.label :reply, "Reply" %><br/>
<%= f.text_field :reply %>
</p>
<p>
<%= f.submit 'Submit' %>
</p>
<% end %>
Partial rendered to show replies in on discussion page:
<h3><%= post.user.first_name %></h3>
<%= post.reply %>
Posted: <%= post.created_at.strftime("%b. %d %Y") %></p>
<p><%= link_to "Delete Comment", [post.discussion, post], data: {confirm: "Are you sure you wish to delete?"}, method: :delete, :class => "post_choices" %></p>
Just want to mention that I also have the correct associations between the three models (User, Discussion, Post). If there is more code needed, please let me know. I appreciate it very much for any information that may be helpful =)
Joe
EDIT
class User < ActiveRecord::Base
has_many :articles
has_many :discussions
has_many :posts
# ...
end
class Discussion
belongs_to :user
has_many :posts
extend FriendlyId
friendly_id :subject, use: :slugged
end
class Post
belongs_to :discussion
belongs_to :user
end
I could post the entire user model if needed but its all validations/devise aspects =P The other two I listed all of the contents in the models.
Edit 2
Thanks to Max, the user_id returns correctly in the console but still not the discussions. Going go dig around a bit more with the recent changes to see what else =)
There are a few issue you need to deal with.
First you should ensure that Devise is actually authorizing your controller action.
class PostsController < ApplicationController
before_filter :authenticate_user!
end
Otherwise current_user will return nil if there is no signed in user. And I'm
guessing that you do not want un-authenticated users to be able to create posts.
Also if you have a nested route you most likely want to check that the discussion actually
exists before trying to add posts.
class PostsController
before_filter :authenticate_user!
before_filter :set_discussion, only: [:new, :create, :destroy]
private
# Will raise an ActiveRecord::NotFoundError
# if the Discussion does not exist
def set_discussion
#discussion = Discussion.friendly.find(params[:id])
end
end
When you are creating resources be careful not to query the database needlessly.
This especially applies to CREATE and UPDATE queries which are expensive.
def create
#post = Post.create(post_params) # INSERT INTO 'users'
#post.discussion_id = params[:discussion_id]
#post.user = current_user
#post.save # UPDATE 'users'
flash.notice = "It has been posted!"
redirect_to discussions_path(#post.discussion)
end
Also you are not even checking if the record was created successfully.
So lets put it all together:
class PostsController
before_filter :authenticate_user!
before_filter :set_discussion, only: [:new, :create, :destroy]
def new
#post = #discussion.post.new
end
def create
# new does not insert the record into the database
#post = #discussion.post.new(create_params) do |post|
post.user = current_user
end
if #post.save
redirect_to #discussion, notice: "It has been posted!"
else
render :new # or redirect back
end
end
def destroy
#post = #discussion.posts.find(params[:id])
#post.destroy
flash.notice = "Deleted"
redirect_to discussion_path(#discussion)
end
private
def create_params
# Only permit the params which the user should actually send!
params.require(:post).permit(:reply)
end
# Will raise an ActiveRecord::NotFoundError
# if the Discussion does not exist
def set_discussion
#discussion = Discussion.friendly.find(params[:id])
end
end