I have a form to create a new event for an organisation. The routing is:
resource :organisations do
resource :events
end
When I edit an event, on success I route back to the show action in the events controller as such:
def update
#organisation = current_user.organisations.find(params[:organisation_id])
#event = #organisation.events.find(params[:id])
if #event.update_attributes(params[:event])
# Handle a successful update.
flash[:success] = "Event updated"
redirect_to organisation_event_path
else
render 'edit'
end
end
When I create an event, I also want it to redirect to the events show action, implemented as follows:
def create
#organisation = current_user.organisations.find(params[:organisation_id])
#event = #organisation.events.create(params[:event])
if #event.save
flash[:success] = "Event added!"
redirect_to organisation_event_path
else
render 'new'
end
end
However, this produces the following error: No route matches {:action=>"show", :controller=>"events"}
This is because the URI does not have the event ID / name in it, as far as I can figure out. It is probably my lack of understanding of path generation, but how do I achieve the result I want?
I am not sure why your first redirect worked, but organisation_event_path will require two arguments since it is creating path from the ids of two resources - organization and event. So, you should call it like this:
organisation_event_path(#organization, #event)
Related
Context is a check-out process, where a user can pick from her/his shipping addresses. Thus a controller action states:
#shipping_addresses = ShippingAddress.where(['user_id = ?', current_user.id])
I find it more appealing to have a boolean value for preferred so that when the check-out process is repeated by the user, s/he is presented with the preferred address. But the question lies in changing this preferred attribute.
In short, a radio button needs to be created to set preferred to true in a form (is there anyway to have on selected, deslecting the other ones?) and then the form needs to update records
def update_preferred
respond_to do |format|
if #shipping_addresses.save
#shipping_addresses = ShippingAddress.where(['user_id = ?', current_user.id]).update_all("preferred = 'false'")
-- selected_shipping_address.update("preferred = 'true'")
end
..also having a hard time with the syntax for declaring the form (as I've never steered away from the standard rails conventions...):
<%= form_for(#shipping_addresses) do |f| %>
One working solution. In the form, include the radio button, which allows only one selection to the user:
<%= radio_button_tag "shipping_address_id", shipping_address.id %>
<%= submit_tag "update" %>
with the defined route to the action, include the method in the controller, which first sets all the found set to false, then the selected shipping_address to true.
def update_preferred
#shipping_address = ShippingAddress.find(params[:shipping_address_id])
#shipping_addresses = ShippingAddress.where(['user_id = ?', current_user.id]).update_all("preferred = 'false'")
#shipping_address.update_column(:preferred, "true")
respond_to do |format|
if #shipping_address.update_attributes(params[:shipping_address])
format.html { redirect_to cart_url(session[:cart_id]), notice: 'Shipping address was successfully updated.' }
else
format.html { redirect_to cart_url(session[:cart_id]), notice: 'Shipping address was not updated.' }
end
end
end
Maybe this can be simplified more. Comments welcome.
I have a custom page in my admin built with active admin:
In app/admin/stats.rb:
ActiveAdmin.register_page 'Stats' do
controller do
def index
#foo = 'bar'
end
end
end
And in app/views/admin/stats/index.html.erb:
<h1> Admin stats</h1>
<p><%= #foo %></p>
When I go to /admin/stats, I see my page, but without the normal admin layout (like on the dashboard page)
How can I decorate my page with the default layout ?
I've tried:
ActiveAdmin.register_page 'Stats' do
content do
'foobar'
end
controller do
def index
#foo = 'bar'
end
end
end
But this doesn't change anything. Still my stat page with no layout. Any ideas ?
This is one way to do it:
In app/admin/stats.rb:
ActiveAdmin.register_page 'Stats' do
content do
render 'index'
end
controller do
def index
#foo = 'bar'
end
end
end
And rename app/views/admin/stats/index.html.erb to app/views/admin/stats/_index.html.erb (Notice the _)
And it works fine.
From what I understand, if index.html.erb is present in views/admin/stats, the content block is not called. If index.html.erb is renamed into something else, then we go into the content block, then the layout rendering is called...
I don't quite get why this is happening, maybe someone of you does... here it goes.
I've created a nested resource:
resources :order do
resources :ordered_vehicles
end
I've added a link_to the new action and passed the order.id like so new_order_ordered_vehicle_path(order.id) the page is loaded nicely. The problem is after I press the button to submit the choice. He switches the path from http://localhost:3000/order/3/ordered_vehicles/new to http://localhost:3000/order/R076027535/ordered_vehicles and displays error Couldn't find Order with id=R076027535... go figure.
The error is being raised in the controller in this method
private
def find_order
#order = Order.find(params[:order_id])
end
Which is a before_filter.
the new.html.haml file looks like this
= form_for [#order, #ordered_vehicle], html: { multipart: true } do |f|
= #order.number
%br= #order.id
= f.fields_for :vehicles do |car|
.... #some fields here
= car.submit "Save your choice"
That id he can't find is the #order.number but I don't get why the switch.
EDIT:
Just to be thorough, I'll add the controller methods:
def new
#ordered_vehicle = #order.ordered_vehicles.build(params.slice(:order_id, :vehicle_id))
end
def create
binding.pry
#ordered_vehicle = #order.ordered_vehicles.build(params.slice(:order_id, :vehicle_id))
if #ordered_vehicle.save
flash[:notice] = "Save successful."
redirect_to account_path
end
end
POST request (I hope that's the one, still new to all this stuff):
"action_dispatch.request.formats"=>[text/html]},
#request_method="POST", #filtered_parameters={"utf8"=>"✓", "authenticity_token"=>
"Ar4vy8pqCSpA2ch0qG0qiJXAJUbNALYxm/FbuKbdzCc=", "ordered_vehicle"=>
{"vehicles"=> {"maker_id"=>"", "model_id"=>"", "year"=>"", "body"=>"", "capacity"=>"", "id"=>"1"}},
"commit"=>"Save your choice", "action"=>"create",
"controller"=>"spree/ordered_vehicles", "order_id"=>"R076027535"}, #method="POST",
#fullpath="/order/R076027535/ordered_vehicles">
As per request ;)
Well, it turned out to be a problem with Spree which I'm currently tweaking (I know I didn't mention it explicitly, but didn't want to just post too much information).
Bottom line:
In the Order model the method to_param was overwritten to pass the number column in to the params. Didn't overwrote it again, just left it there and adapted. In my find_order method I wrote:
def find_order
#order = Order.find_by_number(params[:order_id])
end
Also I've stored the order number in the table, there was a problem I believe with out that, but can't remember explicitly. Anyways, thanks for the help.
P.S. Sorry for the mess
I'm using newes Rails 3 version with will_paginate.
#videos = user.youtube_videos.sort.paginate :page => page
I also added the ##per_page attribute to my youtube_video-model.
But it just won't paginate it. I get always all items in the collection listed.
What have I done wrong?
Yours, Joern.
Why are you calling sort here? That seems unnecessary, and probably would result in it finding all videos and calling pagination on that rather than paying any attention to any variable defined in your Video model. Instead, move the sorting logic into the Video model by using a scope or use the order method.
Here's my solution, my own answer, for all other's having trouble with will_paginate and reading this issue:
Create an ApplicationController method like this:
def paginate_collection(collection, page, per_page)
page_results = WillPaginate::Collection.create(page, per_page, collection.length) do |pager|
pager.replace(collection)
end
collection = collection[(page - 1) * per_page, per_page]
yield collection, page_results
end
Then in your Controller, where you got the collection that should be paginated:
page = setup_page(params[:page]) # see below
#messages = Message.inbox(account)
paginate_collection(#messages, page, Message.per_page) do |collection, page_results|
#messages = collection
#page_results = page_results
end
And in your View:
<% #messages.each do |message| %>
<%# iterate and show message titles or whatever %>
<% end %>
<%= will_paginate #page_results %>
To get the page variable defined, check this:
def setup_page(page)
if !page.nil?
page.to_i
else
1
end
end
So page = setup_page(params[:page]) does the trick, with that simple method.
This WORKS!
I have a nested form and once I save, I want to be able to click a link on the show page to copy or clone that form and open a new one. From there I should be able to make edits (like a new id) and save as a new record. I have seen some examples like this deep_cloneable gem, but I have no idea how to implement it. I think this should be simple, but I just don't understand where to put things in the controller and in the show view.
If you want to copy an activeRecord object you can use its attributes to create new one like
you can have an action in your controller which can be called on link,
def create_from_existing
#existing_post = Post.find(params[:id])
#create new object with attributes of existing record
#post = Post.new(#existing_post.attributes)
render "your_post_form"
end
I found these answers a little hard to follow. One answer shows this:
#post = Post.new(#existing_post.attributes)
which will not work as it will also pass the id, and timestamp values. I used .dup to fix that and I show that in my answer.
Here's how I achieved creating a new item from an existing item.
The model is for a Product, the controller Products_Controller.rb. We're going to add a new action to the controller called copy and we're going to link to it from the show view on an existing Product and render a filled out new view ready to be edited and saved.
First we create a route for the copy action in routes.rb
# Routes.rb
resources :Products do
member do
get 'copy'
end
end
Then a copy action in Products_controller.rb
# ProductController.rb
def copy
#source = Product.find(params[:id])
#product = #source.dup
render 'new'
end
Now we need to add a Link to the show view to call our copy action.
# show.html.erb
<%= link_to "copy", copy_product_path(params[:id]) %>
Rails 4-6 Update:
The strong parameter scaffold makes it even shorter:
# ProductController.rb
# GET /products/1/copy
def copy
#product = #product.dup
render :new
end
And in the erb template:
# show.html.erb
<%= link_to "copy", copy_product_path(#product) %>
class Foo < ActiveRecord::Base
def self.clone_from(parent)
parent = find(parent) unless parent.kind_of? Foo
foo = self.new
foo.attributes = parent.attributes
# if you want to also clone a habtm:
foo.some_association_ids = parent.some_association_ids
# etc.
foo
end
end
class FoosController < ApplicationController
def clone
foo = Foo.clone_from(params[:id])
respond_with(foo)
end
end
Also worth mentioning is the dup method on a model. It makes a copy with all attributes and outgoing relations but sets id to nil. Like this (borrowing code from Naren Sisodiya):
def create_from_existing
#existing_post = Post.find(params[:id])
#create new object with attributes of existing record
#post = #existing_post.dup
render "your_post_form"
end