NoMethodError Rails multiple file uploads - ruby-on-rails

I am working on getting multiple file uploads working for an model in my application, I have included the code below:
delivers_controller.rb
# POST /delivers
def create
#deliver = Deliver.new(params[:deliver])
process_file_uploads(#deliver)
if #deliver.save
flash[:notice] = 'Task was successfully created.'
redirect_to(#deliver)
else
render :action => "new"
end
end
protected
def process_file_uploads(deliver)
i = 0
while params[:attachment]['file_'+i.to_s] != "" && !params[:attachment]['file_'+i.to_s].nil?
deliver.assets.build(:data => params[:attachment]['file_'+i.to_s])
i += 1
end
end
deliver.rb
has_many :assets, :as => :attachable, :dependent => :destroy
validate :validate_attachments
Max_Attachments = 5
Max_Attachment_Size = 5.megabyte
def validate_attachments
errors.add_to_base("Too many attachments - maximum is #{Max_Attachments}") if assets.length > Max_Attachments
assets.each {|a| errors.add_to_base("#{a.name} is over #{Max_Attachment_Size/1.megabyte}MB") if a.file_size > Max_Attachment_Size}
end
assets_controller.rb
class AssetsController < ApplicationController
def show
asset = Asset.find(params[:id])
# do security check here
send_file asset.data.path, :type => asset.data_content_type
end
def destroy
asset = Asset.find(params[:id])
#asset_id = asset.id.to_s
#allowed = Deliver::Max_Attachments - asset.attachable.assets.count
asset.destroy
end
end
asset.rb
class Asset < ActiveRecord::Base
has_attached_file :data,
belongs_to :attachable, :polymorphic => true
def url(*args)
data.url(*args)
end
def name
data_file_name
end
def content_type
data_content_type
end
def file_size
data_file_size
end
end
Whenever I create a new deliver item and try to attach any files I get the following error:
NoMethodError in DeliversController#create
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.[]
/Users/danny/Dropbox/SVN/railsapps/macandco/surveymanager/trunk/app/controllers/delivers_controller.rb:60:in `process_file_uploads'
/Users/danny/Dropbox/SVN/railsapps/macandco/surveymanager/trunk/app/controllers/delivers_controller.rb:46:in `create'
new.html.erb (Deliver view)
<% content_for :header do -%>
Deliver Repositories
<% end -%>
<% form_for(#deliver, :html => { :multipart => true }) do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :caseref %><br />
<%= f.text_field :caseref %>
</p>
<p>
<%= f.label :casesubject %><br />
<%= f.text_area :casesubject %>
</p>
<p>
<%= f.label :description %><br />
<%= f.text_area :description %>
</p>
<p>Pending Attachments: (Max of <%= Deliver::Max_Attachments %> each under <%= Deliver::Max_Attachment_Size/1.megabyte%>MB)
<% if #deliver.assets.count >= Deliver::Max_Attachments %>
<input id="newfile_data" type="file" disabled />
<% else %>
<input id="newfile_data" type="file" />
<% end %>
<div id="attachment_list"><ul id="pending_files"></ul></div>
</p>
<p>
<%= f.submit 'Create' %>
</p>
<% end %>
<%= link_to 'Back', delivers_path %>
Show.html.erb (Delivers view)
<% content_for :header do -%>
Deliver Repositories
<% end -%>
<p>
<b>Title:</b>
<%=h #deliver.caseref %>
</p>
<p>
<b>Body:</b>
<%=h #deliver.casesubject %>
</p>
<p><b>Attached Files:</b><div id="attachment_list"><%= render :partial => "attachment", :collection => #deliver.assets %></div></p>
<%= link_to 'Edit', edit_deliver_path(#deliver) %> | <%= link_to 'Back', deliver_path %>
<%- if logged_in? %>
<%= link_to 'Edit', edit_deliver_path(#deliver) %> |
<%= link_to 'Back', delivers_path %>
<% end %>
_attachment.html.erb (Delivers view)
<% if !attachment.id.nil? %><li id='attachment_<%=attachment.id %>'><a href='<%=attachment.url %>'><%=attachment.name %></a> (<%=attachment.file_size/1.kilobyte %>KB)
<%= link_to_remote "Remove", :url => asset_path(:id => attachment), :method => :delete, :html => { :title => "Remove this attachment", :id => "remove" } %></li>
<% end %>
I have been banging my head against the wall with the error all day, if anyone can shed some light on it, I would be eternally grateful!
Thanks,
Danny

The error message indicates that params[:attachment] is nil inside process_file_uploads(), which causes params[:attachment]['file_'+i.to_s] to raise an exception.
This happens because there's no field named attachment in the form in new.html.erb.

Related

unable to create or update a belongs_to record in Rails 4

I'm new to Rails and struggling to get my belongs_to association right. I have an app where a painting belongs to an artist and an artist can have_many paintings. I can create and edit my paintings, however I can not edit or create artists except through the console. Through much Googling I feel I have got myself turned around. Any help would be much appreciated!
Here's my routes.rb file:
MuseumApp::Application.routes.draw do
resources :paintings
resources :paintings do
resources :artists
resources :museums
end
root 'paintings#index'
end
Here's my paintings Controller
def show
#painting = Painting.find params[:id]
end
def new
#painting = Painting.new
##artist = Artist.new
end
def create
safe_painting_params = params.require(:painting).permit(:title, :image)
#painting = Painting.new safe_painting_params
if #painting.save
redirect_to #painting
else
render :new
end
end
def destroy
#painting = Painting.find(params[:id])
#painting.destroy
redirect_to action: :index
end
def edit
#painting = Painting.find(params[:id])
end
def update
#painting = Painting.find(params[:id])
if #painting.update_attributes(params[:painting].permit(:title, :image)) #safe_params
redirect_to #painting
else
render :edit
end
end
Here's the form in my paintings view:
<%= form_for(#painting) do |f| %>
<fieldset>
<legend>painting</legend>
<div>
<%= f.label :title %>
<%= f.text_field :title %>
</div>
<div>
<%= f.label :image %>
<%= f.text_field :image %>
</div>
<%= form_for([#painting,#painting.create_artist]) do |f| %>
<div>
<%= f.label :Artist %>
<%= f.text_field :name %>
</div>
</fieldset>
<%= f.submit %>
<% end %>
<% end %>
Artists Controller:
class ArtistsController < ApplicationController
def index
#artists = Artist.all
#artists = params[:q] ? Artist.search_for(params[:q]) : Artist.all
end
def show
#artist = Artist.find params[:id]
end
def new
#artist = Artist.new
end
def create
#painting = Painting.find(params[:painting_id])
#artist = #painting.create_artist(artist_params)
redirect_to painting_path(#painting)
end
def destroy
#artist = Artist.find(params[:id])
#Artist.destroy
redirect_to action: :index
end
def edit
#artist = Artist.find(params[:id])
end
def update
#painting = Painting.find(params[:painting_id])
#artist = #artist.update_attributes(artist_params)
redirect_to painting_path(#painting)
end
end
private
def artist_params
params.require(:artist).permit(:name)
end
Index view:
<h1> Hello and Welcome to Museum App</h1>
<h3><%= link_to "+ Add To Your Collection", new_painting_artist_path %></h3>
<%= form_tag '/', method: :get do %>
<%= search_field_tag :q, params[:q] %>
<%= submit_tag "Search" %>
<% end %>
<br>
<div id="paintings">
<ul>
<% #paintings.each do |painting| %>
<li><%= link_to painting.title, {action: :show, id:painting.id} %> by <%= painting.artist_name %></li>
<div id = "img">
<br><%= link_to (image_tag painting.image), painting.image %><br>
</div>
<%= link_to "Edit", edit_painting_path(id: painting.id) %>
||
<%= link_to 'Destroy', {action: :destroy, id: painting.id},method: :delete, data: {confirm: 'Are you sure?'} %>
<% end %>
</ul>
</div>
In your case you should use accepts_nested_attributes_for and fields_for to achieve this.
Artist
has_many :paintings, :dependent => :destroy
accepts_nested_attributes_for :paintings
Painting
belongs_to :artist
And also you should try creating artist with paintings like this
form_for(#artist) do |f| %>
<fieldset>
<legend>Artist</legend>
<%= f.label :Artist %>
<%= f.text_field :name %>
<%= fields_for :paintings, #artist.paintings do |artist_paintings| %>
<%= artist_paintings.label :title %>
<%= artist_paintings.text_field :title %>
<%= artist_paintings.label :image %>
<%= artsist_paintings.text_field :image %>
</fieldset>
<%= f.submit %>
<% end %>
Note:
You should be having your Artist Controller with at least new,create,edit and update methods defined in it to achieve this.
Edit
Try the reverse
Artist
has_many :paintings, :dependent => :destroy
Painting
belongs_to :artist
accepts_nested_attributes_for :paintings
form_for(#painting) do |f| %>
<fieldset>
<legend>Painting</legend>
<%= f.label :title %>
<%= f.text_field :title %>
<%= f.label :image %>
<%= f.text_field :image %>
<%= fields_for :artists, #painting.artists do |ff| %>
<%= ff.label :Artist %>
<%= ff.text_field :name %>
</fieldset>
<%= f.submit %>
<% end %>
Put this form in paintings views.

Ruby Rails submit two forms in one click

I have these forms:
<%= form_for(#user) do |f| %>
<div>
<%= f.number_field :money, :value => #user.money %>
</div>
<% end %>
and
<%= form_for #product, :url => product_path, :html => { :multipart => true } do |f| %>
<div>
<%= f.label :count, 'How Many product?' %><br />
<%= f.number_field :count, :value => "1" %>
</div>
<div>
<%= f.submit('submit') %>
</div>
<% end %>
is there any way to submit this two at once when clicking submit button ? Thanks!
A service object might be a good way to approach this.
class Order
include ActiveModel::Model
attr_accessor :money, :count
def initialize(user=nil, product=nil)
#user = user
#product = product
#money = user.money
#count = 1
end
def persisted?
false
end
def save
// this code needs to save to db
end
end

Can't mass-assign protected attributes: quantities

I'm getting this error:
Can't mass-assign protected attributes: quantities
I looked up all the threads concerning this issue in the site, but couldn't find something to answer my problem. Here are the code snippets:
product.rb
class Product < ActiveRecord::Base
attr_accessible :name, :quantities_attributes
has_many :quantities
accepts_nested_attributes_for :quantities, :allow_destroy => :true,
:reject_if => proc { |attrs| attrs.all? { |k, v| v.blank? } }
new.html.erb
<% if #was_submitted %>
<%= form_for(:new_product_array, :url => products_path) do |f| %>
<% prefix ||= 0 %>
<% #new_product_array.each do |n| %>
<% n.quantities.build %>
<% prefix += 1 %>
<%= f.fields_for(prefix.to_s ) do |child| %>
<div class="field">
<%= child.label :name %><br />
<%= child.text_field :name%>
</div>
<%= render :partial => 'quantities/form',
:locals => {:form => child} %>
<% end %>
<% end %>
<div class="actions">
<%= submit_tag :submit %>
</div>
<% end %>
<% else %>
<%= form_tag new_product_path, :method => 'get' do %>
<p align=center>
How many Items are you Adding? (1-100)
<%= number_field_tag 'amount', 1, :in => 1...100 %>
</br>
To which storage?
<%= number_field_tag 'storage', 1, :in => 1...100 %>
<%= submit_tag "Next", :name => 'submitted' %>
</p>
<% end %>
<% end %>
<%= link_to 'Back', products_path %>
product_controller.rb
def new
#product = Product.new
if params['submitted']
#was_submitted = true
#amount_form = params['amount']
#new_product_array = []
(1..#amount_form.to_i).each do
#new_product_array << Product.new
end
#storage_form = params['storage']
else
#was_submitted = false
end
respond_to do |format|
format.html # new.html.erb
format.json { render :json => #product }
end
end
def create
i=0
logger.info params[:new_product_array].inspect
params[:new_product_array].each do |new_product|
if new_product.last[:name] != nil
#new_product_array[i] = Product.new(new_product.last)
#new_product_array[i].save
i+=1
end
end
redirect_to(products_path)
end
quantity.rb
class Quantity < ActiveRecord::Base
belongs_to :product
attr_accessible :amount, :storage
end
quantity/_form.html.erb
<%= form.fields_for :quantities do |quant| %>
<div class="field">
<%= quant.label :storage %><br />
<%= quant.number_field :storage %>
</div>
<div class="field">
<%= quant.label :amount %><br />
<%= quant.number_field :amount %>
</div>
<% unless quant.object.nil? || quant.object.new_record? %>
<div class="field">
<%= quant.label :_destroy, 'Remove:' %>
<%= quant.check_box :_destroy %>
</div>
<% end %>
<% end %>
Overall what Im trying to do, is ask the user how much products to add, then make a form with the number of fields the user specifies and with one submit button add all of the products, whereas when you add a product you also add a quantity record which holds more information on the product.
You need a line like this:
attr_accessible :name, :quantities_attributes, :quantities
You have very bad code, it can be much simplier 100%.
Your problem is that form dont' know nothing about your resource (product), so it can't render 'smartly' fields "quantities_attributes", it renders "quantities" instead.

Many-to-Many Association with Formtastic not working for me

My use case is a bit more complicated than the one shown in RailsCasts.
I get an unknown attribute: user error.
Issues and users are related by a many-to-many through another model.
I HAVE specified the accepts_nested_attributes_for in my Issue model.
My view code:
<% semantic_form_for #issue do |form| %>
<% form.inputs do %>
<%= form.input :description, :input_html => { :rows => 5, :cols => 1, :class => 'autogrow' } %>
<%= form.input :location %>
<%= form.input :issue_type %>
<% end %>
<% form.inputs :for => :user do |user_form| %>
<%= user_form.input :email %>
<% end %>
<% form.buttons do %>
<%= form.commit_button "Submit" %>
<% end %>
<% end %>
My Controller code:
def create
#issue = Issue.new(params[:issue])
if #issue.save
flash[:notice] = "Thank you"
else
render :action => 'new'
end
end
Any ideas?
Thanks!
Try using #user instead of :user in the user_form.

Newbie stuck again.. No errors, but my data won't save

has_many build method, Rails
Was my last question. It now works: no errors.
The new problem is that the new unit conversion doesn't associate with an ingredient ID. I thought this was "just supposed to work" from the build method?
Unit Conversion controller:
def new
#ingredient = Ingredient.find(params[:ingredient_id])
#unit_conversion = #ingredient.unit_conversions.build
end
def create
#ingredient = Ingredient.find(params[:ingredient_id])
#unit_conversion = #ingredient.unit_conversions.build(params[:unit_conversion])
if #unit_conversion.save
flash[:notice] = "Successfully created unit conversion."
redirect_to ingredient_unit_conversions_url(#ingredient)
else
render :action => 'new'
end
end
Unit Conversion Model:
class UnitConversion < ActiveRecord::Base
belongs_to :ingredient
end
Ingredient Model:
class Ingredient < ActiveRecord::Base
belongs_to :unit
has_many :unit_conversions
end
Thanks for the help, I'm finding a rough learning curve today :)
EDIT:
One more important thing..
new.html.erb
<h1> New Derived Unit </h1>
<% form_for([#ingredient, #unit_conversion]) do |f| %>
<% f.error_messages %>
<p>
<%= f.label :name %>
<%= f.text_field :name%>
</p>
<p>
<%= f.label :conversionToBase%>
<%= f.text_field :conversionToBase%>
</p>
<p>
<%= f.submit "Create" %>
</p>
<% end %>
<% link_to 'Back', url_for( :controller => 'ingredients', :action => 'show', :id => #ingredient)%>
Make sure that you have form_for [#ingredient, #unit_conversion] in your form.
new.html.erb
<% title "New Unit Conversion" %>
<%= render :partial => 'form' %>
<p><%= link_to "Back to List", ingredient_unit_conversions_path(#ingredient) %></p>
_form.html.erb
<% form_for [#ingredient, #unit_conversion] do |f| %>
<%= f.error_messages %>
<p>
... fields here
</p>
<p><%= f.submit "Submit" %></p>
<% end %>

Resources