This might be impossible to answer since there are probably too many variables here, but I thought I'd give it a shot since I always find other answers here. I am still fairly new to rails.
So, I have a bills model/controller/view. I want to create a new bill. I will be editing out the things that shouldn't matter much, but if they are needed I can add them in - just don't want a wall of text.
In the route:
map.resources :bills
My new method in the controller:
def new
#bill = Bill.new
#submit_txt = "Create"
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #bill }
end
end
my form:
<% form_for(#bill) do |f| %>
<%= f.error_messages %>
### form elements here, this all seems fine ####
<p>
<%= f.submit #submit_txt %>
</p>
<% end %>
my create method in the controller:
def create
is_weekly = false
is_monthly = false
#bill = current_user.recurring_bills.build(params[:bill])
#bill.year = #current_year
#errors = 'checking this out'
if #errors.blank?
logger.info "no errors, supposedly; going to save"
### do saving stuff here####
else
logger.info "errors not blank"
render :action => :new
end
end
For some reason this always renders /bills instead of /bills/new. It used to work and I don't know what I did wrong, but now it's not. I get the same response with render :template => 'bills/new'. It goes to the right page with a redirect, but then it won't fill in the form with old values.
The log:
Processing BillsController#create (for 127.0.0.1 at 2010-07-21 21:00:47) [POST]
Parameters: {"commit"=>"Create", "action"=>"create", "authenticity_token"=>"Kc7/iPKbfJBKHHVARuN7K6207tW6Jx4OUn7Xb4uSB8A=", "bill"=>{"name"=>"rent", "month"=>"", "amount"=>"200", "alternator"=>"odd", "day"=>"35", "frequency"=>"monthly", "weekday"=>""}, "controller"=>"bills"}
User Load (0.6ms) SELECT * FROM "users" WHERE ("users"."remember_token" = 'dd7082c56f5a252d14e4e68c528eb26551875c647f998c15d16a064cb075d63c') LIMIT 1
errors not blank
Rendering template within layouts/application
Rendering bills/new
Rendered bills/_form (14.5ms)
Rendered layouts/_stylesheets (3.3ms)
Rendered layouts/_header (5.7ms)
Rendered layouts/_footer (0.3ms)
Completed in 174ms (View: 30, DB: 1) | 200 OK [http://localhost/bills]
Hopefully someone has an idea of what I've done wrong, or I guess I'm starting over.
run a rake:routes from your command line and you will see how they map.
bills GET /bills(.:format) {:controller=>"bills", :action=>"index"}
POST /bills(.:format) {:controller=>"bills", :action=>"create"}
new_bill GET /bills/new(.:format) {:controller=>"bills", :action=>"new"}
edit_bill GET /bills/:id/edit(.:format) {:controller=>"bills", :action=>"edit"}
bill GET /bills/:id(.:format) {:controller=>"bills", :action=>"show"}
PUT /bills/:id(.:format) {:controller=>"bills", :action=>"update"}
DELETE /bills/:id(.:format) {:controller=>"bills", :action=>"destroy"}
The RESTful resources take a little getting used to but in your case \bills with a post method goes to the create action. You are specifying in the create action to render the contents of the new template when you call render :action => :new - you do not actually run the action.
Try this:
render :new
From the docs:
Using render with :action is a frequent source of confusion for Rails newcomers. The specified action is used to determine which view to render, but Rails does not run any of the code for that action in the controller. Any instance variables that you require in the view must be set up in the current action before calling render.
Give this a shot and let us know how it goes. Also, if you render "new", remember that your new action creates a new Bill object and there won't be any old values for it to fill in. I think what you really want to do is render :edit. And in your edit action find the Bill object with the params that you pass to the action.
Related
I'm learning Rails and have encountered some behavior I don't understand. I'm trying to create a simple CRUD app. On the 'new' view after data entry fields and submit button I am trying to add a link that will go to another page (I'll call it 'fnord').
Instead of linking to fnord, it's going to show.html. Given that rails is convention based I wonder if this is a default behavior of some kind, but I haven't been able to figure out what triggers it or the proper way of routing to fnord.
This is Rails 3.2.21, Ruby 1.9.3. I originally generated the model, views and controller using scaffold and then started tweaking things.
Here's my controller (minus unrelated actions - index, destroy, etc):
class EmployeesController < ApplicationController
def show
#employee = Employee.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #employee }
end
end
def new
#employee = Employee.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #employee }
end
end
def fnord
#nothing yet - just trying to get the page to appear right now
end
Here's the relevant code from new.html.erb:
<h2>New Employee</h2>
<%= render 'form' %>
<!-- content, data entry fields, submit button, etc -->
<div>
<%= link_to 'Fnord', employees_fnord_path %>
<div/>
Here's the view, fnord.hmtl.erb:
<h2>Fnord</h2>
<br/>
<p>this is fnord.html</p>
Here's my routes.rb:
MyApp::Application.routes.draw do
resources :employees
get 'employees/fnord'
root :to => 'employees#new'
end
I ran 'rake routes' and get the following output:
employees GET /employees(.:format) employees#index
POST /employees(.:format) employees#create
new_employee GET /employees/new(.:format) employees#new
edit_employee GET /employees/:id/edit(.:format) employees#edit
employee GET /employees/:id(.:format) employees#show
PUT /employees/:id(.:format) employees#update
DELETE /employees/:id(.:format) employees#destroy
employees_fnord GET /employees/fnord(.:format) employees#fnord
root / employees#new
As far as I can see this all looks right, but when I click on the link in new.html it returns show.html, not fnord.html.
The url that shows up in the browser is even locahost:3000/employees/fnord, but the content is from show.html.
I read the routing documentation (http://guides.rubyonrails.org/routing.html) but didn't see any explanations.
I've tried restarting rails (many times) and clearing my browser cache as well and those steps didn't help.
Can anyone enlighten me as to what I'm missing? Thank you very much.
The route for /employees/fnord matches the show route because the show route is looking for URL's in the format: /employees/:id
When you go to /employees/fnord it interprets fnord as the ID of some employee, and it routes it to the show method.
One way to fix it is to declare the /employees/fnord route before the resources routes, so that it matches first:
MyApp::Application.routes.draw do
get 'employees/fnord'
resources :employees
root :to => 'employees#new'
end
Another way is to simply change the name of the URL so that it doesn't match the pattern of /employees/:id:
MyApp::Application.routes.draw do
resources :employees
get 'employees1/fnord', to: 'employees#fnord'
# or whatever
get 'foo', to: 'employees#fnord'
# etc...
get 'employees/foo/fnord', to: 'employees#fnord'
root :to => 'employees#new'
end
I have some extra, non-Restful actions in a restful controller. One action responds to a GET, with a form, the form does a POST/PATCH (I've tried both), to another action. If the requirements aren't met, then the method that responds to a POST adds error messages to the model, and renders the first action, exactly like edit and update, or new and create. But...
If the second action has to render back to the first action I see in my logs that it does it right, but then immediately afterwards, there's a request to GET the second action, which obviously fails because there's no GET path to the second action. The end result is that I don't get errors passed down to the form. Why is this? What could be causing it? It's driving me nuts.
Here's the code
def finish_subscription
unless user_signed_in?
store_location_for :user,request.path
redirect_to new_user_session_path
else
#user ||=User.find_by(subscribe_token: params[:token])
authorize! :finish_subscription,#user
#user.accepted_t_and_c = false
end
end
def confirm_subscription
#user=User.find_by(subscribe_token: params[:token])
authorize! :confirm_subscription,#user
if #user.update_attributes(user_params)
o = #user.orders.create
o.build_a_default_subscription
o.save
session[:active_order_id] = o.id
#user.subscribe_token = nil
#user.save
redirect_to pay_via_braintree_path
else
# I've tried all combinations of render action: 'finish_subscription', render :finish_subscription & etc
render 'finish_subscription'
end
end
This is what I see in my logs;-
Started POST "/users/confirm_subscription/d6bb1ffbe56207b6c6864e739e9a455b"
When that fails on validation errors it gets to the line
render 'finish_subscription'
which responds with ;-
Rendered users/finish_subscription.html.haml within layouts/application (2.4ms)
Which is what I expect. But then, immediately afterwards ...
Started GET "/users/confirm_subscription/d6bb1ffbe56207b6c6864e739e9a455b"
WTF!!! Of course that fails because there's no GET route for that action.
Why why why is this happening? And why is it happening on a sunny Sunday afternoon when I've got better things to do?
I have a rails project with the following route:
get 'login', to: 'user_sessions#new', as: :login
In my UserSessionsController I have
def create
#user_session = UserSession.new(params[:user_session])
respond_to do |format|
if #user_session.save
# Do all the happy stuff
else
format.html { render :action => 'new' }
format.xml { render xml: #user_session.errors, status: :unprocessable_entity }
end
end
end
That's working ok, except that when the user enters incorrect parameters the route is via /user_sessions instead of /login, which is untidy (and means my test assertions are confusing).
Obviously I could just redirect_to login_path, but then my #user_session.errors don't seem to be available so by page doesn't show what was wrong.
How do I redirect back to /login and still have the errors show?
Edit:
It looks as if Rails makes this difficult because it's something I shouldn't try to do. The RESTful path isn't really something the user cares about so I shouldn't be using it as part of my UI testing. Instead, I am looking at the actual content of the rendered page, which the user does care about. Thanks all.
You can add
post 'login', to: 'user_sessions#create', as: :post_login
and change the form action accordingly.
This is happening because when you get validation errors in your form then you are on create action and not new action. Your create action simply render your new actions template with errors, it doesn't send a request to server and hence your url remains same so to fix it you can simply change the route for your create action to this:
post 'login', to: 'user_sessions#create', as: :login
Update:
You'll just have to change your route for create action and then make changes in your form, something like this:
<%= from_for #resource, url: login_path do |f| %>
// form fields
<% end %>
If you'll inspect your form you'll see that its method is POST so when you'll submit it, your form will send a POST request and when you hit /login in your browsers address bar it'll send a GET request so in first case you'll go to create action and in second one you'll go to new action
I'm trying to add an action called rollback to controller.
As I've seen, the only things I should do is writting the new action:
def rollback
puts "ROLLBACK!"
respond_to do |format|
format.html # index.html.erb
format.json { render json: #components }
end
Modify the routes.rb file:
resources :components do
collection do
post :rollback, :as => 'rollback'
end
end
And calling the action from some view:
<%= link_to 'Rollback', rollback_components_path %>
But I get the following error:
Couldn't find Component with id=rollback
app/controllers/components_controller.rb:18:in `show'
That's because instead of going to rollback action, the controller thinks that we are trying to 'show' to component with id 'rollback'.
Something that it seems weird for me is that calling 'new' action rails uses new_component_path (without s, in singular), but if I write rollback_component_path it throws me an error and I cant see the view.
In your routes you require a POST, just clicking a link is by default a GET, so either write
resources :components do
collection do
get :rollback
end
end
and then the link_to will work as expected.
I am assuming the rollback operation is not idempotent, so a POST is semantically better in that case.
If you write your link as follows, then rails will create an inline form for you:
link_to 'Rollback', rollback_components_path, :method => 'post'
Hope this helps.
This will work
routes.rb
resources :components
match "components/rollback" => "components#rollback", :as => :rollback
In views
<%=link_to 'Rollback', rollback_path%>
Ok ill be honest, i haven't spent much time looking for a solution yet seeing as how my son is keeping my attention running around. Either way I would like to ask a question for something that seems pretty simple but has stumped me thus far.
So to keep it simple lets say I have Users(w/model) and Home controllers, Home is the root route.
In the root directory I want to be able to see all posts made by the User using ajax to update a partial on the home page with the list of posts.
In the users controller I have a def called userposts with this in it
def userposts
#user = User.find_by_id(params[:id])
#userposts = #user.posts.all(:order => "created_at DESC")
respond_to do |format|
format.js { #userposts}
end
end
And in my view I have
<p id="aboutuser">
<% if #user.about? %>
<%= " " + #user.id.to_s %>
<% else %>
User has not yet filled this out.
<% end %>
</p>
<h3 id="authpostlink">
<%= link_to "List of all posts", user_userposts_path(#user.id), :id => #user.id, :remote => true %>
</h3>
my errors are as follows
Started GET "/users/2/userposts" for 127.0.0.1 at Sun Jan 15 13:36:23
-0600 2012 Processing by UsersController#userposts as JS Parameters: {"user_id"=>"2"} User Load (0.1ms) SELECT "users".*
FROM "users" WHERE "users"."id" IS NULL LIMIT 1 Completed 500 Internal
Server Error in 1ms
NoMethodError (undefined method posts' for nil:NilClass):
app/controllers/users_controller.rb:27:inuserposts'
Rendered
/home/n0de/.rvm/gems/ree-1.8.7-2011.03/gems/actionpack-3.1.0/lib/action_dispatch/middleware/templates/rescues/_trace.erb
(0.8ms) Rendered
/home/n0de/.rvm/gems/ree-1.8.7-2011.03/gems/actionpack-3.1.0/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb
(0.8ms) Rendered
/home/n0de/.rvm/gems/ree-1.8.7-2011.03/gems/actionpack-3.1.0/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (3.2ms)
I do realize i did not post the _show.js.erb file that calls the action to update the div but according to the error messages it doesn't seem the process has gotten that far.
Assuming you have the following:
# /app/models/user.rb
class User < ActiveRecord::Base
has_many :posts
end
# /app/models/post.rb
class Post < ActiveRecord::Base
belongs_to :user
end
I would add a nested resource in your routes file:
#/config/routes.rb
resources :users do
resources: posts
end
You get a bunch of great "_path" methods for free (run $ rake routes from your console to see them all), and it gives you access to URLs such as /users/123/posts. This request will go to the index method of your PostsController and will automatically include :user_id => 123 in the params hash. You can then do the following:
# In your view:
<%= link_to "List of all posts", user_posts_path(#user), :remote => true %>
<div id="posts"></div>
# /app/controllers/posts_controller.rb
class PostsController < ApplicationController
respond_to :js # allows for AJAX requests
def index
if params[:user_id].present? # Do this if using the nested resource
#user = User.find(params[:user_id])
#posts = #user.posts.order('posts.created_at DESC')
else # Otherwise, treat it like a normal request
#posts = Post.all
end
respond_with #posts
end
end
Because the your request is sent remotely, you need a corresponding "js" version of your index view (note the file name below and see this Railscast for more explanation):
# /app/views/posts/index.js.erb
$('#posts').html("<%= escape_javascript(render(#posts)) %>");
This will render out the posts into that <div id="posts"> tag. (You'll probably need a "_post.html.erb" partial in /app/views/posts/" as well.)
However, having said all this, are you sure you need to do this via AJAX? You could simply preload all the posts in the UsersController#show method, initially hide the list using CSS, and then add a jQuery toggle() method on that link. Anyway, hope this makes sense and is helpful.