I have made my form:
<tbody>
<% #player.bases.each do |basis| %>
<td><%= basis.id %></td>
<td><%= image_tag(basis.image_url(:thumb), class: 'thumbnail') %></td>
<td><%= link_to basis.name, basis %></td>
<td><%= basis.cost %></td>
<td><%= basis.short_info %></td>
<td>
<%= form_for #player, url: {:controller => 'players', :action => :remove_detail} do |f| %>
<%= f.hidden_field :type, :value => 'basis' %>
<%= f.hidden_field :detail_id, :value => basis.id %>
<%= f.submit 'Remove',class: 'btn btn-danger' %>
<% end %>
</td>
<% end %>
</tbody>
In my routes, I have added this:
resources :players do
collection do
get 'search'
post 'remove_detail'
end
end
I have remove_detail in my players_controller.rb, and I have added this action to before_action to get current player. However when I press on my Remove button, it throws me error and tries to run update action of my controller. Why?
My before_action:
before_action :set_player, only: [:show, :edit, :update, :destroy, :remove_detail]
My remove_detail:
def remove_detail
type = params['type']
id = params['detail_id']
if type == 'basis'
basis = Basis.find(id)
name = basis.name
#player.bases.delete(basis)
end
redirect_to #player, notice: "#{name} detail is removed"
end
To fix that, try as follows:
First of all, I'd redefine your routes as follows:
resources :players do
member do
delete 'remove_detail'
end
collection do
get 'search'
end
end
This will generate proper url for deleting a detail for a "single Player":
/players/:id/remove_detail
Because of REST-y nature of Rails, we defined the url to be accessible by performing delete request.
Your form change accordingly:
<%= form_for #player, { url: { action: "remove_detail" }, method: :delete } do |f| %>
Changing your routes to use delete method is more to keep the convention of Rails. Post would make your application work too, but - its just Rails-y way.
Good luck!
Related
I have an app with products - each product has things like notes/FAQs/attachments.
I can delete the notes & FAQs successfully, but not the Active Storage attachments.
Could somebody please assist? I've tried using a separate method in the Products controller but that didn't work, so my current line is using an Uploads controller.
The current error I am getting is:
NameError in UploadsController#destroy
uninitialized constant Upload
Uploads controller:
class UploadsController < ApplicationController
load_and_authorize_resource :nested => :product
def destroy
#product = Product.find(params[:id])
#upload = #product.ActiveStorage::Attachment.find(params[:id])
#upload.purge
redirect_back(fallback_location: products_path)
end
end
Products view:
<% #product.uploads.each do |upload| %>
<% if can? :destroy, upload %>
<td><%= link_to t('X', :default => t("X")),
product_upload_path(#product, upload),
:method => :delete,
:data => { :confirm => t('.confirm', :default => 'Are you sure you want to delete this attachment?') },
:id =>'delete-faq' %></td>
<% end %>
<% if upload.variable? %>
<span><%= image_tag upload.variant(resize: "100x100"), class: "other-image" %></span>
<% elsif upload.previewable? %>
<span><%= link_to image_tag(upload.preview(resize: "100x100"), class: "other-image"), rails_blob_path(upload), target: "_blank" %></span>
<% else %>
<span><%= link_to image_tag("paper.jpg", size: "100x100", class: "other-image"), rails_blob_path(upload), target: "_blank" %></span>
<% end %>
<% end %>
routes:
resources :products do
resources :notes
resources :faqs
resources :uploads
end
I know this is not the most RESTful solution but I got it working, so am adding this as an answer in case it helps anybody else tearing their hair out.
I have checked the logs and it does remove both the attached image and the blob.
In my case, a product has_many uploads.
routes.rb:
resources :products do
resources :uploads do
match '/remove', to: 'products#remove', via: 'delete'
end
end
products controller (yes, I know, putting it here is not ideal)
def remove
#product = Product.find(params[:product_id])
#upload = #product.uploads.find(params[:upload_id])
#upload.purge
redirect_to product_path(#product), notice: "Upload was successfully removed."
end
products show view:
<% #product.uploads.each do |upload| %>
<%= link_to "X", product_upload_remove_path(#product, upload),
:method => :delete,
:data => { :confirm => t('.confirm', :default => 'Are you sure you want to delete this upload?') },
:id =>'delete-faq', :class => 'delete' %></td>
<% end %>
Hopefully this will help others or give a foundation for a more RESTful solution.
Background
I'm implementing a feature in my application that allow users to rate and review pictures.
My problem stems from nesting Reviews resources inside Pictures resources, specifically in my Review#Edit function. The error message specifically declares missing required keys: [:id]
Error Message
ActionController::URLGenerationError in Reviews#Edit
No route matches {:action=>"show", :controller=>"reviews", :format=>nil, :picture_id=>#<Review id: 4, username: "DarkMouse", body: "Updating review", picture_id: 11, created_at: "2014-08-14 03:26:52", updated_at: "2014-08-14 03:55:29">, :id=>nil} missing required keys: [:id]
Undefined method 'reviews_path' for #<#<Class:0x45c1b00>:0x39ae810>
Extracted source (Around line #7):
4 <div class = 'center'>
5 <div class = 'center'>
6
7 <% form_for [:picture, #review] do |f| %>
8
9 <p>
10 <%= f.label :username, :class => 'marker' %><br />
I searched for answers on Stack Overflow.com(My references are given at the bottom) and was advised to do this.
<td><%= link_to 'Edit', edit_picture_review_path(#picture, review) %></td>
Every single solution I came across said to edit the path to include the specific object. If this is any indication, it looks like it really should work.
Parameters
{"picture_id"=>"11",
"id"=>"4"}
Also, my path_url is as follows
http://localhost:3000/pictures/11/reviews/4/edit
The URL path looks similar to my defined routes as well
edit_picture_review_path GET /pictures/:picture_id/reviews/:id/edit(.:format) reviews#edit
I am using a Posts/Comments relationship model for a Pictures/Reviews relationship.
Models
class Review < ActiveRecord::Base
belongs_to :picture
end
class Picture < ActiveRecord::Base
has_many :reviews
end
Above, I established a one-to-many relationship between pictures and reviews.
Routes
favorite_picture_path PUT /pictures/:id/favorite(.:format) pictures#favorite
picture_reviews_pat GET /pictures/:picture_id/reviews(.:format) reviews#index
POST /pictures/:picture_id/reviews(.:format) reviews#create
new_picture_review_path GET /pictures/:picture_id/reviews/new(.:format) reviews#new
edit_picture_review_path GET /pictures/:picture_id/reviews/:id/edit(.:format) reviews#edit
pictures_review_path GET /pictures/:picture_id/reviews/:id(.:format) reviews#show
PATCH /pictures/:picture_id/reviews/:id(.:format) reviews#update
PUT /pictures/:picture_id/reviews/:id(.:format) reviews#update
DELETE /pictures/:picture_id/reviews/:id(.:format) reviews#destroy
ReviewsController
class ReviewsController < ApplicationController
before_action :set_review, only: [:show, :edit, :update, :destroy]
before_filter :find_picture, only: [:index, :create, :edit, :update, :destroy]
def edit
#review = Review.find(params[:id])
end
def update
#review = Review.find(params[:id])
if #review.update_attributes(params[:review])
flash[:notice] = "Review updated"
redirect_to #picture
else
flash[:error] = "There was an error updating your review"
redirect_to #picture
end
end
private
def set_review
#review = Review.find(params[:id])
end
def find_picture
#picture = Picture.find(params[:picture_id])
end
def review_params
params.require(:review).permit(:username, :body, :post_id)
end
end
The problem might lie with the review_params
post_id #should be
review_id
Reviews#Index Page
<h3>Reviews for <%= "#{#picture.title}" %></h3>
<table class = "reviews-table">
<thead>
<tr>
<th>Review</th>
<th>Username</th>
<th><th>
</tr>
</thead>
<tbody>
<% #picture.reviews.each do |review| %>
<tr>
<td><%= review.body %></td>
<td><%= review.username %></td>
<td><%= link_to 'Edit', edit_picture_review_path(#picture, review) %></td>
</tr>
<% end %>
<div class = 'center'>
<p><%= link_to 'New Review', new_reviews_path(#review), :class => "btn btn-info" %></p>
<p><%= link_to 'Back', picture_path, :class => "btn btn-info" %></p>
</div>
Reviews#Edit Page
<h3>Edit Review</h3>
<div class = 'center'>
<div class = 'center'>
<%= form_for [:picture, #review] do |f| %>
<p>
<%= f.label :username, :class => 'marker' %><br />
<%= f.text_field :username %>
</p>
<p>
<%= f.label :body, "Review", :class => 'marker' %><br />
<%= f.text_area :body, :rows => "10", :cols => "10" %>
</p>
<p>
<%= f.submit "Submit Review", :class => 'btn btn-success' %>
</p>
<% end %>
routes.rb
resources :pictures do
put :favorite, on: :member
resources :reviews
end
As you can see above, I am using nested resources.
References and External Links
Missing required keys
ActionController::UrlGenerationError missing required keys: [:id]
No route matches missing required keys: [:id]
ActionController::UrlGenerationError: missing required keys: [:id]
No routes matches "missing required keys: [:id, :_id]"
All of the above links and more consulted. Every solution tried but none worked. This is why I am re-opening this question. I hope this guide can serve as a template for this problem moving forward.
Thanks in advance. This is not an easy question. Good luck.
A few observations, off the cuff.
Your error message indicates that No route matches {:action=>"show", :controller=>"reviews".
Your ReviewsController does seem to be missing the show action, but that does not seem relevant.
So that leaves us with the offending line:
<% form_for [:picture, #review] do |f| %>
Note, your error is not with this line:
<td><%= link_to 'Edit', edit_picture_review_path(#picture, review) %></td>
Looking at the docs, under Resource-oriented style, we can see the following instruction:
If your resource has associations defined, for example, you want to
add comments to the document given that the routes are set correctly:
<%= form_for([#document, #comment]) do |f| %>
...
<% end %>
For you, this translates to:
<% form_for [#picture, #review] do |f| %>
The same page also advises:
In the examples just shown, although not indicated explicitly, we
still need to use the :url option in order to specify where the form
is going to be sent. However, further simplification is possible if
the record passed to form_for is a resource, i.e. it corresponds to a
set of RESTful routes, e.g. defined using the resources method in
config/routes.rb. In this case Rails will simply infer the appropriate
URL from the record itself
Although you shouldn't need it, for you this translates to something like:
<% form_for [#picture, #review], url: edit_picture_review_path(#picture, #review) do |f| %>
I'm trying to allow a user to read, update, or destroy a form that they themselves have created. I'm having some trouble.
In my ability.rb, I have
can [:update, :read, :destroy], Form.where(:user => user)
given that user is an attribute of the form. I can get the forms (and the responses to the forms) to display when I don't have this line in my ability class, but I obviously do not want people to be able to delete the forms of other users. I have already called
load_and_authorize_resource
to invoke CanCan in the controller. Currently I have this error:
The can? and cannot? call cannot be used with a raw sql 'can' definition. The checking code cannot be determined for :index Form(id: integer, user_id: integer, policy: boolean, two_adult: boolean, training: boolean, attribute_name: boolean, attribute: boolean, agree_to_form: boolean, user_signature: string, signature_date: date, printed_date: datetime, created_at: datetime, updated_at: datetime)
(Changed some names for privacy. I'm also using Devise, by the way, if this changes anything)
Any help would be greatly appreciated. Thanks!
Edit: Would a solution such as...
can :read, Form do |form|
form.user == user
end
be a possibility?
Edit2: My view...
<% #forms.each do |form| %>
<tr>
<td><%= form.user %></td>
<td><%= form.policy %></td>
<td><%= form.two_adult %></td>
<td><%= form.training %></td>
<td><%= form.other_attribute %></td>
<td><%= form.attribute %></td>
<td><%= form.agree_to_covenant %></td>
<td><%= form.user_signature %></td>
<td><%= form.signature_date %></td>
<td><%= form.printed_date %></td>
<% if can? :read, #form %>
<td><%= link_to 'Show', form %></td>
<% end %>
<% if can? :update, #form %>
<td><%= link_to 'Edit', edit_form_path(form) %></td>
<% end %>
<% if can? :destroy, #form %>
<td><%= link_to 'Destroy', form, method: :delete, data: { confirm: 'Are you sure?' } %></td>
<% end %>
</tr>
<% end %>
I also noticed upon refreshing the page that the user hash changes each time. This is problematic.
Edit3: Pertinent Ability.rb info...
if user.is_camper?
can :read, Camp
can :read, Payment, Payment.where(:user => user)
can :create, Payment
can :read, Form, :user => user.id
Here is a visual of what happens too.
Your last variant (defined through block) should work.
See examples here.
In your case you can define through hash - it is simpler
can :read, Form, :user_id => user.id
And according to this point you can separately define :read, :create, :update and :destroy
Code on the page:
You iterate by form, so you should check ability on each of them (NOT for one #form)
<% #forms.each do |form| %>
...
<% if can? :read, form %>
...
<% end %>
<% if can? :update, form %>
...
<% end %>
<% end %>
Changes in ability:
can :read, Form # user can read all forms (not only own)
can [:create, :update, :destroy], Form, :user_id => user.id # it is better to use `user_id` instead of `user`
It is just one variant that should work properly but it is not tested by me :)
From show view: I'd like to pass the shown message's id to discard action and trash the message.
From index view: I'd like to pass the checked messages' ids to discard action and trash them all at once.
But I only can trash one record at once even if I check multiple and submit from index view.
How can I archive both 1 and 2 with the same action????
Routes
match 'messages/discard(/:id)' => 'messages#discard', :via => :post , :as => :discard_messages
index view
<%= form_tag(:action => discard, :via => 'post') do %>
<% #messages.each do |m| %>
<tr>
<td><%= check_box_tag "id",m.id %></td>
<td><%= m.last_message.id %></td>
<td><%= 'unread' if m.is_unread?(current_user) %></td>
<td><%= m.last_message.created_at.to_s(:jp) %></td>
<td><%= m.last_sender.username %></td>
<td><%= link_to m.subject, show_messages_path(:id => m, :breadcrumb => #box) %></td>
</tr>
<% end %>
<%= submit_tag "discard", :class => 'btn' %>
<% end %>
show view
<%= link_to 'Discard', discard_messages_path(#messages), :class => 'btn', :method => 'post' %>
controller
def discard
conversation = Conversation.find_all_by_id(params[:id])
if conversation
current_user.trash(conversation)
flash[:notice] = "Message sent to trash."
else
conversations = Conversation.find(params[:conversations])
conversations.each { |c| current_user.trash(c) }
flash[:notice] = "Messages sent to trash."
end
redirect_to :back
end
use the [] naming in your html, which rails will then make available as an array in the params
index.html.erb
<td><%= check_box_tag "message_id[]", m.id %></td>
controller
# ...
else
conversations = Conversation.where("id IN (?)", params[:message_id][])
# ...
to simplify things further I would remove the conditional in your action and create two separate actions
routes
resource :messages do
member do
post 'discard' # /messages/:id/discard
end
collection do
post 'discard_all' # /messages/discard_all?message_id[]=1&message_id[]=22
end
end
What is causing this error when I click on the edit button for a note? The delete button works fine. I created the note object with a scaffold.
index.html.erb
<% #notes.each do |note| %>
<%= note.detail %>
<%= button_to 'Delete', note, :confirm => 'Are you sure?', :method => :delete %>
<%= button_to 'Edit', edit_note_path(note) %>
<% end %>
notes_controller.rb
before_filter :check_ownership, :except => [:new, :create, :index, :edit]
def edit
#note = Note.find(params[:id])
end
Error
ActiveRecord::RecordNotFound in NotesController#192
Couldn't find Note with ID=edit
../app/controllers/notes_controller.rb:248:in `check_ownership'
Parameters:
{"id"=>"edit"}
EDIT
config/routes.rb
map.resources :notes
All of the other routes for notes work fine.
Thanks for reading
Scaffold controller's 'edit' action, and defult routing support 'edit' as GET rather than POST request.
If you use link_to rather than button_to, things should work.
Alternatively, change the line containing button_to -
<%= button_to 'Edit', edit_note_path(note), :method => :get %>