Rails 3 and paperclip on update method issue - ruby-on-rails

Paperclip is working well to save a user avatar but I find an issue on update.
In my view, if the user has an image saved in the model it will show an image tag with the current image next to the file upload field so you can see what your current avatar is.
If the image has no changes but the model validation fails (like no first_name), the original display image disappears, meaning that the user either has to correct the error and re-select an image and submit (update) or go back and start over without the error. Any ideas? Thanks in advance.
Heres the code:
Model
class User < ActiveRecord::Base
# Validation
validates :first_name, :presence => true
# Paperclip
has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "70x70#" }
end
Controller
...
# GET /users/1/edit
def edit
#user = User.find(params[:id])
end
# POST /users
# POST /users.xml
def create
#user = User.new(params[:user])
respond_to do |format|
if #user.save
format.html { redirect_to(#user, :notice => 'User was successfully created.') }
format.xml { render :xml => #user, :status => :created, :location => #user }
else
format.html { render :action => "new" }
format.xml { render :xml => #user.errors, :status => :unprocessable_entity }
end
end
end
# PUT /users/1
# PUT /users/1.xml
def update
#user = User.find(params[:id])
respond_to do |format|
if #user.update_attributes(params[:user])
format.html { redirect_to(#user, :notice => 'User was successfully updated.') }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #user.errors, :status => :unprocessable_entity }
end
end
end
...
View
<%= form_for #user, :html => {:multipart => true} do |f| %>
<div class="row text">
<%= f.label :first_name %>
<div class="field">
<%= f.text_field :first_name %>
</div>
</div>
<div class="row">
<%= f.label :avatar %>
<div class="field">
<%= image_tag #user.avatar.url(:thumb) %>
</div>
<div class="field" id="avatar_upload">
<%= f.file_field :avatar %>
</div>
</div>
<div class="row actions"><%= f.submit %> or <%= link_to 'cancel', users_path %>.</div>
<% end %>

This is being caused by your form sending 'nil' to the server (due to the field being blank since a new avatar is not being uploaded) when your page sends a POST request to your server. Short of mucking things up with hidden temporary fields, there are a few ways around this:
Client-side validation. Prevent your page from submitting if anything is wrong and you won't end up reaching the server-side validation failure that results in the missing image.
Move the avatar field to a separate FORM object on the same page that only handles the avatar. That will ensure that the page only ever sends avatar information in a POST message if it is related to an actual avatar edit.
Move the avatar upload/update functionality to a completely separate page that only handles the avatar. This works for the same reason the reason above this one works.

Related

Belongs_To / Has Many Relationship

I have the following:
Clients have many Reports and Reports belong to a client.
However on the creation of the Report it is not assigning the client_id into the database, but not sure why?
Am i doing something wrong here?
Client Model
class Client < ActiveRecord::Base
has_many :reports, :dependent => :destroy
end
Report Model
class Report < ActiveRecord::Base
has_attached_file :report
belongs_to :client
end
Client Controller (Update)
# PUT /clients/1
# PUT /clients/1.json
def update
#client = Client.find(params[:id])
respond_to do |format|
if #client.update_attributes(params[:client])
format.html { redirect_to [:admin,#client], :notice => 'Client was successfully updated.' }
format.json { head :ok }
else
format.html { render :action => "edit" }
format.json { render :json => #client.errors, :status => :unprocessable_entity }
end
end
end
Report Controller (Create)
# POST /reports
# POST /reports.json
def create
#report = Report.new(params[:report])
#report.client_id = params[:client][:client_id]
respond_to do |format|
if #report.save
format.html { redirect_to '/admin/clients', :notice => 'Report was successfully created.' }
format.json { render :json => #report, :status => :created, :location => #report }
else
format.html { render :action => "new" }
format.json { render :json => #report.errors, :status => :unprocessable_entity }
end
end
end
Client Edit View
<%= form_for([:admin, #client.reports.build]) do |f| %>
<label class="formlabel">Report Upload</label>
<%= f.file_field :report, :class=>"text-input small-input" %>
<div class="actions">
<br />
<%= f.submit 'Upload', :class => 'button' %>
</div>
<% end %>
Assistance would be appreciated!
I'm curious; because you're using .build in the form_for, the client may already be in the url.
What if you remove:
#report.client_id = params[:client][:client_id]
and submit, what happens then? Because this line is looking incorrectly at the params, so I wonder if you are overwriting that you built in the form_for
Either that, or a hidden field like #Adam said would work.
The client_id doesn't have a related input field in the form on your view. You could add something to your form like:
f.hidden_field :client_id
And then in your controller, set it as:
#report.client_id = params[:report][:client_id]
Alternatively, you could include the client_id in the url.
Stupid Mistake it seems needed to up the end function on the form-for for the client to close it off before opening the form-for the reports.
Then add the field for the client_id and now just hide the field as per Adam suggestion.
Thanks Steph for suggestions as this help me solve this mistake.
Thanks Everyone! :-)

How to avoid the error undefined method 'mail' for nil:NilClass

If I leave the input box blank. I get this error everytime. I don't want it to make new record when it's blank. when not, I want it to make new record.
this input box is nested and the code of controller is written like this to avoid error
def create
# Check if there is any contact info added
if params[:girl][:contact_attributes][:mail].empty?
params[:girl].delete(:contact_attributes)
end
#girl = Girl.new(params[:girl])
respond_to do |format|
if #girl.save
format.html { redirect_to #girl, notice: 'Girl was successfully created.' }
format.json { render json: #girl, status: :created, location: #girl }
else
format.html { render action: "new" }
format.json { render json: #girl.errors, status: :unprocessable_entity }
end
end
end
view is like this
<%= form_for(#girl) do |f| %>
....
<div class="field">
<%= f.label :mail %><br />
<%= f.fields_for :contact_attributes, #girl.contact do |contact| %>
<%= contact.text_field :mail %>
<% end %>
</div>
....
<div class="actions">
<%= f.submit %>
</div>
<% end %>
my model
class Girl < ActiveRecord::Base
has_many :users
has_one :contact
accepts_nested_attributes_for :contact
attr_accessible :id, :name_en, :name_ja, :gender_id, :contact_attributes, :photo, :tag_list
searchable do
text :name_en, :name_ja
text :contact do
contact.mail
end
end
has_attached_file :photo,
:styles => {
:thumb=> "100x100>",
:small => "400x400>" }
acts_as_taggable_on :tags
acts_as_commentable
end
You have to set
#girl = Girl.new
inside your else block, just before
format.html { render action: "new" }
The error happens because you render the new template and inside it the form_for(#girl) gets a nil object - #girl. In order to render the line <%= f.label :mail %><br /> it tries to call the mail method on the given #girl object in order to get its default value. Since the #girl object is nil and not set in the create action before you render the new template you get this error.
UPDATE:
I misunderstood your situation in the answer on the first part of this post. The solution in my opinion is redirecting to the new girl path instead of just rendering the new action. While rendering only renders the view redirecting will make a full-stack request process. Assuming you have the route new_girl_path set you should replace format.html { render action: "new" } with
format.html { redirect_to new_girl_path }
You can run `rake routes and see what named routes you have set.
I problem is the following few lines of code.
if params[:girl][:contact_attributes][:mail].empty?
params[:girl].delete(:contact_attributes)
end
If mail is empty in user contact you have removed the contact attributes and created only the user object.
So if you call #girl.contact you will get nil.
I don't know why you have removed the contact attributes.If you still want to do it you need to add one more line.
if #girl.save
format.html { redirect_to #girl, notice: 'Girl was successfully created.' }
format.json { render json: #girl, status: :created, location: #girl }
else
#Assuming you have the association like: user has_one contact
#user.build_contact
format.html { render action: "new" }
format.json { render json: #girl.errors, status: :unprocessable_entity }
end
And one more thing
<%= f.fields_for :contact_attributes, #girl.contact do |contact| %>
can be simply written as
<%= f.fields_for :contact do |contact| %>
Replace same line of code with <%= form_for( :girl, :url => {:action => :create}) do |f| %>

Creating view for adding nested resource in Ruby on Rails app

I'm making a RoR app that has 3 resources, that are nested in this order: Projects -> Feeds -> XML_Fields. Projects has_many Feeds, Feeds has_many XML_Fields. My problem is that I am getting stuck on adding to the Show Feeds view the ability to add a new XML_Field. My code is below:
<h2>Add an XML field:</h2>
<%= form_for ([#feed, #feed.xml_fields.build]) do |f| %>
<div class="field">
<%= f.text_area :tag %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Executing this returns a NoMethodError: undefined method 'tag'. Tag is the name of the column in the xml_fields table I created. My xml_fields_controller has this create method:
def create
#xml_field = Feed.find(params[:feed_id]).xml_fields.build(params[:xml_field])
respond_to do |format|
if #xml_field.save
format.html { redirect_to( :back, :notice => 'XML Field was successfully created.') }
format.xml { render :xml => #xml_field, :status => :created, :location => [#xml_field.feed, #xml_field] }
else
format.html { render :action => "new" }
format.xml { render :xml => #xml_field.errors, :status => :unprocessable_entity }
end
end
end
And my routing file looks like this:
resources :projects do
resources :feeds
end
resources :feeds do
resources :xml_fields
end
Any suggestions to make this NoMethodError go away? Thanks.
model XmlField doesn't have tag field in database ;)

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