I'm trying to redirect from one controller to another in Rails and I am getting this error:
undefined method `call' for nil:NilClass
The code is pretty simple (in def create method):
#blog_post_comment = BlogPostComment.new(params[:blog_post_comment])
respond_to do |format|
if #blog_post_comment.save
flash[:notice] = 'Comment was successfully created.'
redirect_to(#blog_post_comment.blog_post)
else
render :action => "new"
end
end
Save goes ok, the value gets into the database. How can I work around the redirect fail?
Form:
<% form_for #blog_post_comment do |f| %>
<%= f.hidden_field :blog_post_id %>
...
UPD:
After some investigation, it turned out that problem was in the line respond_to do |format| in the blog_post_comment controller. Once I removed it, everything is OK now.
Assuming you have an association, you can find your comment like this:
#blog_post = BlogPost.find(params[:blog_post_id])
#blog_post_comment = #blog_post.comments.build(params[:blog_post_comment])
And then
respond_to do |format|
if #blog_post_comment.save
flash[:notice] = 'Comment was successfully created.'
redirect_to(#blog_post)
else
render :action => "new"
end
end
If you don't have an association, here's how you set it up:
In your BlogPost model, you should have the following line:
has_many :blog_post_comments
And in your BlogPostComment model, you should have:
belongs_to :blog_post
In routes.rb, you should have:
map.resources :blog_post_comment, :has_many => 'blog_post_comments'
Related
I have two models: Schedule and Project. Schedule belongs_To Project and Project has_one schedule. There are several problems, but i think they all have the same cause. Here is the code for the schedules#create controller:
def create
#schedule = Schedule.new(schedule_params)
#schedule.project = Project.find(params[:project_id])
if #schedule.project.student_id == current_user.id
if #schedule.save && #schedule.freelancer_accepts
flash[:notice] = "Successfully created schedule."
redirect_to profile_path(current_user.profile_name)
else
render :action => 'new', :notice => 'Invalid Schedule'
end
else
render :action => 'new', :notice => 'Schedule is invalid.'
end
end
Here are the problems:
Even though it is a has_one relationship, I am still able to create many schedules for a single project. This led me to change my controller to this:
def create
if !Schedule.where(project_id: params[:project_id]).any?
#schedule = Schedule.new(schedule_params)
#schedule.project = Project.find(params[:project_id])
if #schedule.project.student_id == current_user.id
if #schedule.save && #schedule.freelancer_accepts
flash[:notice] = "Successfully created schedule."
redirect_to profile_path(current_user.profile_name)
else
render :action => 'new', :notice => 'Invalid Schedule'
end
else
render :action => 'new', :notice => 'Schedule is invalid.'
end
else
render :action => 'new', :notice => 'A schedule has already been created.'
end
end
The change was that I essentially wrapped the controller in an if that says "if a schedule exists, don't create one. After I changed my code, I got the following problems:
In firefox, when I press the submit button to create a new schedule, i get this error:
First argument in form cannot contain nil or be empty
In Safari, when I press the submit button to create a new schedule, the page turns white. Furthermore, every time I try to go back to the page which displays the form, the page goes white. It is very strange.
How do I fix this? Thanks.
UPDATE: My routes and form view might help:
routes:
resources :projects do
resources :schedules
end
First part of form:
<%= form_for [#project, #schedule] do |f| %>
Turns out this and several other problems i was having was not actually caused by the has_one relationship. There was some type of problem with my cache. I'm not exactly sure what it was, but by disabling the cache I was able to fix the problem.
Each project can have a single page:
resources :project do
resource :page
end
class Project < ActiveRecord::Base
has_one :page
end
class Page < ActiveRecord::Base
belongs_to :project
end
def new
#project = Project.find(params[:project_id])
#page = #project.build_page
respond_to do |format|
format.html
end
end
def create
#project = Project.find(params[:project_id])
#page = #project.build_page(params[:page_id])
respond_to do |format|
if #page.save
format.html { redirect_to #page, :notice => 'Page was successfully created.' }
else
format.html { render action: "new" }
end
end
end
But when I go to save a page, I not only get a routing error, but it doesn't actually save to the db.
Routing Error
No route matches [POST] "/projects/2/pages"
My form action looks like this:
<%= form_for([#job, #page]) do |f| %>
Does anyone have any idea of what is going on? I kind of pieced all of this together from other SO posts, but the more I change a line here or there, I feel like I'm getting further from a working solution. For example, if I change the form action to be:
<%= form_for #page, url: job_page_path(#job) do |f| %>
Everything magically works, but then the edit action is still broken. What basic concept am I butchering?
Thanks!
--Mark
you have a typo:
resource :page
should be
resources :page
(notice the s)
resource (singular) is actually quite a different method that builds a different set of routes. See the docs for more info.
UPDATE / ERRATUM
sorry, i've read your question too fast. you should take a look at Ruby on rails: singular resource and form_for - it seems that form_for does not know how to properly handle singular resources.
Someone here on SO suggests a quick fix for this : nested form_for singular resource
In my Ruby on Rails application, each group has_many :expenses. I have nested my routes, so expenses are entered only as child entities of their parent groups. Here's an excerpt from routes.rb.
resources :groups do
resources :expenses
end
I cannot figure out how to render the 'new' action in the case of an expense not saving when it is submitted through /groups/:group_id/expenses/new. In my expenses_controller.rb, here is how the create action is defined:
def create
#expense = Expense.new(params[:expense])
#expense.group_id = params[:group_id]
if #expense.save
redirect_to group_expense_path(#expense.group.id, #expense.id)
else
render 'new'
end
end
Everything works fine if I satisty expense validation and #expense.save winds up working. However, when it fails and the code tries to render 'new' I get:
undefined method `expenses_path' for #<#<Class:0x007fd408b1fd58>:0x007fd408f21ca8>
So, I am assuming I have something about my nested routing wrong. How do I return the user to the new form but still display to him/her through the flash[] params the errors with the data they originally attempted to submit?
The problem is that #group is not initialized
So in your controller just do
#expense = Expense.new(params[:expense])
#group = Group.find(params[:group_id])
#expense.group_id = #group.id
Looks like you need to explicitly specify the url for form_for in your view.
Something likeā¦
<%= form_for #expense, :url => group_expenses_path(#group.id) do |f| %>
...
<% end %>
In your <%= form_for %> you have used #group for url, because expenses belongs_to groups. But inside your create action in the controller you have not defined what is #group, so first you should define it as:
#expense = Expense.new(params[:expense])
#group = Group.find(params[:group_id])
#expense.group_id = #group.id
Also I would suggest to use respond_to in your controller:
respond_to do |format|
if #expense.save
format.html { redirect_to group_expense_path(#group.id, #expense.id), :notice => "Any msg you want" }
else
format.html { render :action => "new" }
end
end
All of these are in your create action inside the controller.
Also for different rendering methods look up: http://guides.rubyonrails.org/layouts_and_rendering.html
Hope this helps!
I'm having an issue where my article_controller.rb's create method is redirecting to the index when the article.save fails due to invalid input by the user. The articles creation url is /articles/new but when the submit fails, I'm redirected to /articles. The form is still available in /articles exactly as it was on /articles/new. The desired behavior would be to return to the /articles/new with whatever the user may have entered repopulated in the form. Is there a way to do this? Here are some of the code snippets to illustrate what's going on.
Here is the article new method:
def new
#article = Article.new
respond_to do |format|
format.html
end
end
Here is the article create method:
def create
#article = current_user.articles.new(params[:article])
respond_to do |format|
if #article.save
format.html { redirect_to(#article, :notice => 'Article was successfully created.') }
else
format.html { render 'new' }
end
end
end
Here is the form:
<%= form_for(#article) do |f| %>
.....
<% end %>
I'm eventually hoping to get this working with a :remote => :true call in the form_for, but just want to get it working first the way it is. Any suggestions?
Try
format.html { render :action => "new" }
And if you are using Rails 3+, try writing your controller something like this DRY.
class ArticlesController < ApplicationController
respond_to :html
def new
#article = Article.new
respond_with #article
end
def create
#article = Article.new(params[:article])
#article.save
respond_with(#article)
end
end
I'm using Ruby on Rails 2.3.8 and I've got a registration form in which I receive a parameter as follows: /registration/4, which 4 is the id of a user who recommended the user that is about to register in the website.
The problem is that if the validation fails when the user submits the registation (the form renders to the controller users, action create_particular) the site will redirect to /users/create_particular, and therefore I lose the parameter with value 4 that I had before. Besides, I want the user to stay at the same url, which is /registration/4
How can I do that?
Then you should rewrite your create method. You should use redirect_to :back instead of render :action
UPD
def new
#word = Word.new(params[:word])
#word.valid? if params[:word]
end
def create
#word = Word.new(params[:word])
if #word.save
redirect_to #word
else
redirect_to new_word_path(:word => params[:word] )
end
end
Looks quite dirty, but this is just a scratch
UPD 2
This is really not the best solution, but it works
# routes.rb
match 'words/new' => 'words#create', :via => :post, :as => :create_word
# words_controller
def new
#word = Word.new
end
def create
#word = Word.new(params[:word])
respond_to do |format|
if #word.save
format.html { redirect_to(#word, :notice => 'Word was successfully created.') }
else
format.html { render :action => "new" }
end
end
end
# views/words/new.html.erb
<%= form_for(#word, :url => create_word_path) do |f| %>
...
<% end %>
Submit to the current URI (e.g. action=""). When the submission is valid, redirect. POST->Redirect->GET is a good habit.
From the top of my head:
Edit your controller (registrations_controller.rb file). Create method by default contains following piece of code:
if #registration.save
format.html { }
format.xml { }
else
format.html { }
format.xml { }
end
Add redirect_to (:back) between brackets to else format.html{}
Ok I solved the problem by doing the following:
1) I created two routes with the same path, but with different conditions method (one it's post and the other one is set to get)
2) I changed the form in order to post to the POST action defined above
3) I added render => :my_action when the validation fails
So that's pretty much it.
Thanks anyway for all your help.
Hidden field. That user ID param has a name by which you extract it in your controller, right? So just put that value in a hidden field of the same name, then it will survive a round-trip.
For example:
<%= hidden_field_tag :referring_user_id, params[:referring_user_id] %>