How do I update at attribute on multiple models in Rails? - ruby-on-rails

I would like to perform this action with the click of a link.
def index
#books = Book.all
end
def update_read_books
#books.each do |book|
book.update_attribute(:read, true)
end
end
How can I update mark all books as read?

Rails has an update_all method. See link.
def mark_as_read
Book.update_all(read: true)
redirect_to books_path
end
Setup up route to give you /books/mark_as_read
resources :books do
get :mark_as_read, on: :collection
end
Then in your view:
= link_to "Mark all as Read", mark_as_read_books_path
If you really want to be Restful, you can make your route a put/patch method. Don't forget to change your link to the method you choose.
If you want this to be an Ajax request, you can add a remote: true to the link:
= link_to "Mark all as Read", mark_as_read_books_path, remote: true
That will make it async. Then you need to handle that response in the controller.
def mark_as_read
Book.update_all(read: true)
respond_to do |format|
format.html { redirect_to books_path }
format.js
end
end
... and add a template inside /views/books/update_all.js.erb and add some jQuery to remove the notification. For example:
$('#notification_count').hide();

First of all define your method outside index method.
def index
#books = Book.all
end
def update_read_books
Book.update_all(read: true)
end
define route:
resources :books do
put :update_read_books, on: :collection
end
Then in your view:
= form_for update_read_books ,:remote => true do |f|
= f.submit "All Read"
Try with this. Hope it will help.

Related

How do I add a "New" button to ALL ActiveAdmin show pages?

When creating objects in ActiveAdmin I typically need to add multiple, and wish there was an option to add another object on the show page (which appears after submitting the new object).
I have been doing this model by model:
ActiveAdmin.register Color do
action_item :add, only: :show do
link_to "New", new_administration_color_path
end
end
Add this to admin/administration_color.rb
controller do
def create
create! do |format|
format.html { redirect_to admin_administration_color_path(resource, add_more: true) }
end
end
end
and some modified your code
action_item :add, only: :show do
link_to "New", new_administration_color_path if params['add_more'] == true
end

How to call a method from controller using link_to?

I want my app to add a book to current_user when he clicks the link. My code seems to be ok, there are no errors but after user clicks the link nothing happens.
book.rb:
has_many :book_users
has_many :users, through: :book_users
user.rb:
has_many :book_users
has_many :books, through: :book_users
book_user.rb:
belongs_to :book
belongs_to :user
books_controller.rb:
before_action :is_admin?, except: [:book_params, :add_to_books_i_read, :index]
before_filter :authenticate_user!
expose(:book, attributes: :book_params)
expose(:books)
def create
if book.save
redirect_to(book)
else
render :new
end
end
def update
if book.save
redirect_to(book)
else
render :edit
end
end
def add_to_books_i_read(book_id)
current_user.books << Book.find(book_id)
end
In my index view I have
ul
-books.each do |book|
li
= link_to "#{book.name}", book_path(book)
= link_to " Add to my books", {:controller => "books", method: :add_to_books_i_read, book_id: :book.id}
So, what am I doing wrong? Why my method add_to_books_i_read does nothing? The table in database book_users doesn't record anything after clicking this link_to, but it works well itself (I checked via console). What can I do? How to make users add books through the method and how to call this method correctly? Every help would be appreciated, thank you!
= link_to " Add to my books", {:controller => "books", action: :add_to_books_i_read, book_id: :book.id}
The first, method in the link_to is the symbol of HTTP verb so you can not pass your function in the controller
To define and use new action on you controller you need to define route for that see here
Also try to organize your routes as (note member section)
resources :books do
member do
get :add_to_books_i_read
end
end
and see Prefix Verb column in rake routes output.
From #Ioannis Tziligkakis, I edited a bit.
Try:
# config/routes.rb
resources :books do
member do
get :add_to_books_i_read
end
end
# app/controller/books_controller.rb
def add_to_books_i_read
current_user.books << Book.find(params[:id])
# redirect or other logic
end
# app/views/books/index.html.haml
ul
- books.each do |book|
li
= link_to "#{book.name}", book_path(book)
= link_to "Add to my books", add_to_books_i_read_book_path(book.id)
Try this:
# config/routes.rb
resources :books do
# just not about method:
# use get for request, search
# use put for update
# use post for create
# use delete for destroy
put :add_to_books_i_read, on: :member
end
# app/controller/books_controller.rb
def add_to_books_i_read
#book = Book.find(params[:id])
current_user.books << #book
respond_to do |format|
format.js
end
end
# app/views/books/index.html.haml
ul
- books.each do |book|
li{:id => "book_#{book.id}"}
= render "links", book: book
# add field: app/views/books/_links.html.haml
= link_to "#{book.name}", book_path(book)
= link_to "Add to my books", add_to_books_i_read_book_path(book), method: :put, remote: true
# add field: app/views/books/add_to_books_i_read.js.erb
$("#book_<%= #book.id %>").html("<%= j render 'books/links', book: #book %>")
Since add_to_books_i_read is a controller action method you should update your routes to include this action for books resources in order to play nicely with Rails way of doing thing in a resourceful way. You also get to use a URL helper for this path that will look like this:
/books/:id/add_to_books_i_read
Your code could look like this:
# config/routes.rb
resources :books do
get :add_to_books_i_read, on: :member
end
# app/controller/books_controller.rb
def add_to_books_i_read
current_user.books << Book.find(params[:id])
# redirect or other logic
end
# app/views/books/index.html.haml
ul
- books.each do |book|
li
= link_to "#{book.name}", book_path(book)
= link_to "Add to my books", add_to_books_i_read_book_path(book)
Oh God. With your advise help I've finally got it! So,
Controller:
def add_to_books_i_read
current_user.books << Book.find(params[:id])
redirect_to(:back) #this line
end
In routes
resources :books do
member do
get :add_to_books_i_read
end
end
index view
-books.each do |book|
li
= link_to "#{book.name}", book_path(book)
= link_to " Add to my books", add_to_books_i_read_book_path(book)
Thank you all for your help! I'm really really grateful! :)

Routing Error: No route matches [PUT] "/goals/4/edit"

I am trying to create a button to change a model record attribute from false to true. I'm using a form_tag as follows:
=form_tag edit_goal_path(goal), method: :post do
=hidden_field_tag :purchased, value: true
=submit_tag "Purchase"
It's haml, but feel free to post suggestions with ERB. I'm getting the following error:
No route matches [POST] "/goals/4/edit"
Rails.root: /home/ben/rails_projects/hartwig
However, I already have the following route from resources:
PUT /goals/:id(.:format) goals#update
My controller looks as following:
def edit
#goal = Goal.find(params[:id])
end
def update
#goal = Goal.find(params[:id])
if #goal.update_attributes(goal_params)
redirect_to '/goals', notice: "Update successful!"
else
render '/'
end
end
def goal_params
params.require(:goal).permit(:item, :description, :picture, :purchased)
end
How do I get this to work? Or is there a better way to solve this?
Your question says:
I am trying to create a button to change a model record attribute from false to true
so why are you using a form for it? I think a better approach would be to create a link or button that will call an ajax method or a normal method with post route and update your attribute. You can achieve it by following these steps:
a. Create a route for your custom action where you'll update your attribute:
post 'purchase_update/:id' => "goal#update_purchase", as: update_purchase #post as you want to send your goal id
b. create your custom method inside your controller:
def update_purchase
#goal = Goal.find(params[:id])
#goal.update_attribute(:purchased, true)
respond_to do |format|
format.html {redirect_to your_path, notice: 'purchase updated'}
format.js {} #if you want to do something by ajax
end
end
c. Create your link that will call this method:
=link_to "Purchase", update_purchase_path(#goal), method: post
and if you want to do it by ajax then
=link_to "Purchase", update_purchase_path(#goal), method: post, remote: true
Another solution to your problem could be adding a new method to the Goal Controller:
in goals_controller.rb
def purchase
#goal.update_attribute(:purchased, true)
end
and also add on top (just add :purchase)
before_action :set_goal, only: [:show, :edit, :update, :destroy, :purchase]
in routes.rb change to
resources :goals do
member do
post 'purchase'
end
end
to add a new post routes to your goals
now you will have a purchase_goal_path that you can use in your view like this:
link_to 'Purchase', purchase_goal_path(#goal), method: :post

ruby-on-rails - link with custom action on object

I have a list of objects and for each object I have 2 links - "Delete" and "Done". Like this:
1) Buy tomatoes; Delete | Done
2) Wash the car; Delete | Done
But my custom action done don't work. I need your help, how can I make my action and what code should I write in file routes.rb?
Controller:
def done
#task = Task.where(id: params[:id])
#task.done = true
redirect_to "/tasks"
end
Link in view file:
<%= link_to "Done", {:controller => :tasks, :action => :done, :id => task.id}%>
Thanks!
In your controller:
def done
#task = Task.find(params[:id])
#task.done = true
#task.save! # Use the 'bang' method or check the return value
redirect_to tasks_path
end
In routes.rb:
resources :tasks do
get :done, on: :member
end
In your view:
<%= link_to 'Done', done_task_path(task.id) %>
You are assigning the value but not saving the data. So save the data using #task.save
def done
#task = Task.where(id: params[:id])
#task.done = true
#task.save
redirect_to "/tasks"
end

Generating the edit path for a nested resource referenced by multiple models

In routes.rb:
resources :cars do
resources :reviews
end
resources :motorcycles do
resources :reviews
end
In ReviewsController:
before_filter :find_parent
def show
#review = Review.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #review }
end
end
def edit
#review = Review.find(params[:id])
end
# ...
def find_parent
#parent = nil
if params[:car_id]
#parent = Car.find(params[:car_id])
elsif params[:motorcycle_id]
#parent = Motorcycle.find(params[:motorcycle_id])
end
end
Generating the "show" link for a Review is simply (this works):
= link_to "Show", [#parent, #review]
Similarly I would like to reference a generic edit path for a Review, something like (this does not work):
= link_to "Edit", [#parent, #review], :action => 'edit'
Does anyone know if this is possible or, if not, how this might be accomplished?
link_to 'Edit Review', [:edit, #parent, #review]
It turns out the answer I am looking for can be found with the URL helper "edit_polymorphic_path" (see: http://rubydoc.info/github/rails/rails/master/ActionDispatch/Routing/PolymorphicRoutes). In order to get the link I am attempting above I was able to accomplish this with:
edit_polymorphic_path([#parent, #review])
I think what you need here is a polymorphic assocation. Ryan Bates at Railscasts.com explains it perfectly.
http://railscasts.com/episodes/154-polymorphic-association
It will make it easy for you to have things like:
User, Manager, Note
A user can have many notes
A manager can have many notes
A note can belong to a user OR a manager
users/1/notes/edit
managers/1/notes/edit
The Railscast will explain how to do it :)
EDIT:
def edit
#reviewable= find_reviewable
#reviews= #reviewable.reviews
end
private
def find_reviewable
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
nil
end
Then in your link to, it would be something like:
link_to 'Edit Review', edit_review_path([#reviewable, :reviews])
^^ Not tested.

Resources