Cant get validation erros to work in Rails - ruby-on-rails

I cannot get validation errors to work. I have a create product page which has two form_for's One for products and one for photos. When a product doesnt upload you get sent to redirect_to new_product_path
product controller
def new
#product = Product.new
#photo = Photo.new
end
def create
check_for_image = Photo.where(:product_id => nil, :user_id => current_user)
if check_for_images == []
redirect_to products_new_path, :notice => "Add an image then press start before submit"
else
#product = current_user.products.create(params[:product])
if #product.save
Photo.where(:product_id => nil, :user_id => current_user).update_all(:product_id => #product.id)
render "show", notice: "Product created!"
else
redirect_to new_product_path #, :flash => { :error => "Test!" }
# render "new"
end
end
end
I tried to do render "new" instead of redirect_to but i get undefined method `model_name' for NilClass:Class with the error pointing to the photo form for
haml create product page
= form_for #photo, :html => { :multipart => true, :id => "fd" } do |f|
%span Add files...
= f.file_field :image
= form_for #product,:url => products_path, :html => { id: "fd", multipart: true } do |f|
- if #product.errors.any?
.error_messages
%h2 Form is invalid
%ul
- for message in #product.errors.full_messages
%li
= message
%p
= f.text_field :name, placeholder: "Name"
%p
= f.text_field :price, class: "auto", data: { a_sign: "$ " }, placeholder: "Price"
%p
= f.text_field :description, placeholder: "Description"
%p.button.start
= f.submit
product model
validates :user_id, presence: true
validates :name, presence: true, length: { minimum: 5 }
validates :price, presence: true, numericality: { greater_than_or_equal_to: 3.00 }

I think first you need to know difference between redirect_to & render
redirect_to :action => 'new' #It will call the method 'new' and then it will call
#respective file. In this case it is `new.haml`
render :action => 'new' #It will call `new.haml` directly without calling method 'new'
So when you used render :action => 'new' it will not get #photo and hence it is giving error undefined methodmodel_name' for NilClass:Classeither you have to handlenil` on view or
It will fixed when you
Change
redirect_to new_product_path
To
#photo = Photo.new
render :action => 'new'

render will render a particular view using the instance variables available in the action. For example if a render was used for the new action, when a user goes to /new, the new action in the controller is called, instance variables are created and then passed to the new view. Rails creates the html for that view and returns it back to the user’s browser. This is what you would consider a normal page load.
redirect_to will send a redirect to the user’s browser telling it to re-request a new URL. Then the browser will send a new request to that URL and it will go through the action for that URL, oblivious to the fact that it was redirected to. None of the variables created in the action that caused the redirect will be available to the redirected view. This is what happens when you click on ‘Create’ in a form and the object is created and you’re redirected to the edit view for that object.
so you had no validation errors because each time you did a redirect a new instance was created with no errors.
2 lines you have to change:
render "show", notice: "Product created!"
and
redirect_to new_product_path
not sure about this:
redirect_to products_new_path, :notice => "Add an image then press start before submit" it's not clear what it does and how your app should behave.
your controller:
def new
#product = Product.new
#photo = Photo.new
end
def create
check_for_image = Photo.where(:product_id => nil, :user_id => current_user)
if check_for_images == []
redirect_to products_new_path, :notice => "Add an image then press start before submit"
else
#product = current_user.products.create(params[:product])
if #product.save
Photo.where(:product_id => nil, :user_id => current_user).update_all(:product_id => #product.id)
redirect_to #product, notice: "Product created!"
else
render action: "new"
end
end
end
more:
Are redirect_to and render exchangeable?
http://blog.markusproject.org/?p=3313

Related

Rendering a form partial within a different controller in rails

I have an Items model that has_many Photos (which belongs_to Items), both namespaced under Admin.
Uploading via the form works as expected via the Photo's new or edit action(, but not when rendered from the Item show page, I get an "undefined method `model_name' for NilClass:Class" error.When I create a new photo record I pass in the item_id and I'm not sure what to pass into the form on the items page.
PhotosController;
class Admin::PhotosController < Admin::BaseController
def new
#photo = Photo.new(:item_id => params[:item_id])
end
def create
#photo = Photo.new(params[:photo])
if #photo.save
flash[:notice] = "Successfully created photo."
redirect_to [:admin,#photo.item]
else
render :action => 'new'
end
end
def edit
#photo = Photo.find(params[:id])
end
def update
#photo = Photo.find(params[:id])
if #photo.update_attributes(params[:photo])
flash[:notice] = "Successfully updated photo."
redirect_to [:admin,#photo.item]
else
render :action => 'edit'
end
end
def destroy
#photo = Photo.find(params[:id])
#photo.destroy
flash[:notice] = "Successfully destroyed photo."
redirect_to [:admin,#photo.item]
end
end
photos/_form.html.haml;
= form_for [:admin,#photo], :html => {:multipart => true,id: 'upload'} do |f|
= f.hidden_field :item_id
%p
= f.label :name
%br/
= f.text_field :name
%p
= file_field_tag :image, multiple: true, name:'photo[image]',id: 'photo_image'
%p= f.submit
items/show.html.haml;
= render 'admin/photos/form'
doh! figured it out
the render call should be;
= render 'admin/photos/form', :photo => #item.photos.build
and the form
= form_for [:admin,photo]
or you can create a #photo variable in your items_controller#show like
#photo = #item.photos.build
and in your partial
form_for[:admin,#photo]
i think this would be better, because all of your variables get assigned in the controller and not one in the controller and the other in the view.

Runtime error - called id for nil in Rails 3 project

I'm getting this error when I try to submit my form (/POSTS/SHOW):
RuntimeError in Posts#show
Showing /Users/fkhalid2008/loand/app/views/posts/show.html.erb where line #1 raised:
Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id
Extracted source (around line #1):
1: <%= form_remote_tag (:update => 'message', :url => {:controller => 'main', :action => 'send_message', :user_id => #post.user.id}) do %>
2: <br>
3: <br />
4: <br />
How do I fix this?
Relevant code is below:
/VIEWS/POSTS/SHOW
<%= form_remote_tag (:update => 'message', :url => {:controller => 'main', :action => 'send_message', :user_id => #post.user.id}) do %>
<br>
<br />
<br />
<div class="field">
Hello! My name is <%= f.text_field :subject %> and I'm contacting you in response to your ad. I'm interested in learning more so get in touch! Here's my contact details: <%= f.text_field :body %>.
Submit
<% end %>
POST MODEL
class Post < ActiveRecord::Base
belongs_to :user
attr_accessible :title, :job, :location, :salary
validates :title, :job, :location, :salary, :presence => true
validates :salary, :numericality => {:greater_than_or_equal_to => 1}
default_scope :order => 'posts.created_at DESC'
end
USER MODEL
class User < ActiveRecord::Base
has_many :posts
has_one :profile
has_private_messages
attr_accessible :email
validates_presence_of :email
validates_uniqueness_of :email, :message =>"Hmm, that email's already taken"
validates_format_of :email, :with => /^([^\s]+)((?:[-a-z0-9]\.)[a-z]{2,})$/i, :message => "Hi! Please use a valid email"
end
POSTS CONTROLLER
def show
#post = Post.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render :json => #post }
end
end
def new
#post = Post.new
#post.user = current_user
respond_to do |format|
format.html # new.html.erb
format.json { render :json => #post }
end
end
def edit
#post = Post.find(params[:id])
end
def create
#post = Post.new(params[:post])
#post.user = current_user
respond_to do |format|
if verify_recaptcha && #post.save
format.html { redirect_to :action=> "index"}
format.json { render :json => #post, :status => :created, :location => #post }
else
format.html { render :action => "new" }
format.json { render :json => #post.errors, :status => :unprocessable_entity }
end
end
end
def update
#post = Post.find(params[:id])
#post.user = current_user
respond_to do |format|
if #post.update_attributes(params[:post])
format.html { redirect_to #post, :notice => 'Post was successfully updated.' }
format.json { head :ok }
else
format.html { render :action => "edit" }
format.json { render :json => #post.errors, :status => :unprocessable_entity }
end
end
end
APPLICATION CONTROLLER (this is where I am defining current_user)
class ApplicationController < ActionController::Base
protect_from_forgery
private
def current_user
#_current_user ||= session[:current_user_id] &&
User.find_by_id(session[:current_user_id])
end
end
MAIN CONTROLLER (send_message is defined here)
class MainController < ApplicationController
def send_message
message = Message.new
message.subject = params[:subject]
message.body = params[:message]
message.sender = User.find session[:user]
message.recipient = User.find params[:user_id]
if message.save
ContactMailer.deliver_message_email message.recipient.email, message.id, request.host
return redirect_to "/posts"
else
render :text => "Hmm. Something seems to be wrong...let me look into it"
end
end
You don't have a user assigned to the post record represented by the #post instance variable.
Presumably a user needs to be logged in to make a post?
Also presumably you have a current user defined somewhere?
Your controller actions that use this form need to assign the user to the post record
def new
#post = Post.new
#post.user = current_user # You will need to get the current user from somewhere
respond_to do |format|
format.html # new.html.erb
format.json { render :json => #post }
end
end
UPDATE
To make sure that your current user is assigned you should add a check to ensure the user is logged in in the controller actions. This is normally done by adding a before filter to authorize the current user which will redirect back to the login page if the current use is logged out.
Have a look at this rails cast to explain logging in and out and redirecting on a before filter http://railscasts.com/episodes/250-authentication-from-scratch
There is a revised version of the cast here but you will need a subscription for that
http://railscasts.com/episodes/250-authentication-from-scratch-revised
well worth paying for IMO
End of update
You will need to / should also assign the current user in whatever actions update the post record - i.e. the create and update actions in EXACTLY the same way.
Also, because you have not got a user assigned to a post record then you need to handle this scenario in the form so that you don't get 500 errors
You can use the #post.user.blank? boolean check to help you with this
Something like
<% if #post.user.blank? %>
<h2>There is no user assigned to this post record! This should never happen ad you should never see this message, please contact support if etc... </h2>
<% else %>
<!-- Place all your current form code here -->
<% end %>
You are getting the error because #post.user is nil in :user_id => #post.user.id.
Make sure you define #post in your post controller's show action and that it has a valid user association.

How do I fix an unexpected nil object error?

I have this error when I try to add a comment via AJAX:
ActionView::Template::Error (You have a nil object when you didn't expect it!
You might have expected an instance of ActiveRecord::Base.
The error occurred while evaluating nil.to_key):
1: $("#<%= dom_id(#comment.comment_title) %>").parent().append('<%= escape_javascript(render(#comment)) %>');
2: $(".comment_form")[0].reset();
app/views/comments/create.js.erb:1:in `_app_views_comments_create_js_erb___503385093_2173588800_0'
app/controllers/comments_controller.rb:8:in `create'
The form to create the comment has an association option that should set the comment_title for the comment:
<%= simple_form_for([#video, #video.comments.new], :remote => true) do |f| %>
<%= f.association :comment_title, :collection => #video.comment_titles.map {|ct| [ct.title, comment_title_path(ct)] }, :label => "Comment Title:", :include_blank => false %>
<%= f.input :body, :label => false, :placeholder => "Post a comment." %>
<%= f.button :submit, :value => "Post" %>
<% end %>
Here's the create action in my comments controller:
def create
#comment = #video.comments.new(params[:comment].merge({:user_id => current_user.id}))
if #comment.save
respond_to do |format|
format.html { redirect_to :back }
format.js
end
else
respond_to do |format|
format.html { redirect_to :back, :alert => "Unable to add comment." }
format.js { render 'fail_create.js.erb' }
end
end
end
Why am I getting this error? How can I resolve this?
You can't use this:
dom_id(#comment.comment_title)
You can use this
dom_id(#comment)
Because you should pass a record, but not a string
http://apidock.com/rails/ActionController/RecordIdentifier/dom_id
UPD
as far as comment_title is a has_one association (but not an attribute, as I thought), so the problem is it isn't created so it passes nil into dom_id method

Error handeling w/ form_for in associated resource

I can't seem to get the flow to work right here. I have a Ruby on Rails (2.3.9) application. For the purposes of this question we have only a couple of resources. Boxes and Messages.
Box has_many :messages
Message belongs_to :box
I have created a view located at /boxes/1/new_message where I have the below form_for code. I can successfully create a message from this view. The problem arrises when my validations kick in.
In this case, message.body can't be blank and is validated by message.rb. Once this validation happens, it kicks the user over to the Message.new action and upon successfully filling in the message.body the app can no longer find the #box.id to place in message.box_id.
I have tried just about everything I can think of by not sure how to allow a users to receive a validation and still successfully create a message for a box. See my code below for reference.
/views/boxes/new_message.html.erb
<% form_for [#box, Message.new] do |f| %>
<%= f.error_messages %>
<%= f.label :message_title %>
<%= f.text_field (:title, :class => "textfield-message grid_12 alpha") %>
<%= f.label :message_body %>
<%= f.text_area (:body, :class => "textarea-message grid_12 alpha ") %>
<%= f.submit "Add a Message", :class => 'input boxy' %>
<% end %>
messages_controller.rb
def create
#message = Message.new(params[:message])
#box = Box.find(params[:box_id])
#message = #box.messages.build(params[:message])
#message.user = current_user
respond_to do |format|
if #message.save
flash[:notice] = 'Message was successfully created.'
format.html {redirect_to #box }
else
format.html { render :action => "new" }
format.xml { render :xml => #flash.errors, :status => :unprocessable_entity }
end
end
end
def new
#message = Message.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #message }
end
end
I believe your
#box = Box.find(params[:box_id])
should be
#box = Box.find(params[:id])

Rails does not display error messages on a form in a custom method

I've created a custom method called checkout in my app. I create an order (which is done my adding products to my "cart"), assign it to my client, and then I head to my checkout screen where I confirm the items and enter their customer order number and complete the order (submit).
Everything works great except that it doesn't display error messages. I'm able to display a flash error notice (seen in complete_order method) when things go wrong but it doesn't specify the details like a normal form would. The error messages should appear if the customer order number is not unique for that client.
Below is the custom method (checkout) related code.
Order Model:
validates_uniqueness_of :customer_order_number, :scope => :client_id
Orders_controller:
def checkout
#order = current_order
end
def complete_order
#order = current_order
respond_to do |format|
if #order.update_attributes(params[:order])
#order.complete #sets submitted datetime and state to 'complete'
flash[:notice] = 'Thank you! Your order is being processed.'
format.html { redirect_to( products_path ) }
format.xml { head :ok }
else
flash[:error] = 'Please review your items' #added to confirm an error is present
format.html { redirect_to( checkout_path ) }
format.xml { render :xml => #order.errors, :status => :unprocessable_entity }
end
end
end
And the form in the checkout view:
<% form_for #order, :url => { :controller => "orders", :action => "complete_order" } do |f| %>
<%= f.error_messages %>
<%= f.text_field :customer_order_number, :label => "Purchase Order Number" %>
<p>
<%= f.submit 'Complete Order', :confirm => 'Are you sure?' %> <small> or <%= link_to 'cancel', current_cart_path %></small>
</p>
<% end %>
Any idea how I can display the specific error messages?
Change redirect_to to render in else condition otherwise checkout method get called again & no error will displayed.
else
format.html { render :action => 'checkout' }

Resources