Rails 3 - Create View to Insert Multiple Records - ruby-on-rails

I have what seems like a simple query. I need to create a view that will accept multiple records based on a single model. In my case the model is Project, which has 1 foreign key (person) and 2 fields time, role. I need to create a view (form) to insert 5 roles.
<%= form_for(#project) do |f| %>
<% 5.times do |index|%>
<div class="field">
<%= f.label :position %><br />
<%= f.text_field "fields[#{index}][stime]" %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
I get an error message: undefined method `fields[0][stime]'
I do not think the railscasts for nested models is what I need.
How would I go about creating this?
EDIT: The Project model code is below:
class Project < ActiveRecord::Base
belongs_to :person
attr_accessible :role, :stime
end
The Projects_Controller code for the new method is below:
def new
#project = Project.new
end

I see you're planning to make some 1-to-many relationship (Product has_many :roles).
Here's some advices.
First, take a look at the accepts_nested_attributes_for method. You need to add it to your model to be able to perform mass-create.
Second, fields_for is what you need to design nested forms.
I'll give you some example of mass-creating for a simple Product has_many :line_items case:
<%= form_for #product do |f| %>
<%= f.fields_for :line_items, [LineItem.new]*5 do |li_fields| %>
<%= li_fields.text_field :quantity %>
<%= li_fields.text_field :price %>
<br>
<% end %>
<%= f.submit "Create line items" %>
<% end %>
All you need is to write in you controller something like:
#product.update_attributes params[:product]
and 5 line_items will be created at once.
Don't forget to white-list association_attributes (see params in your logs to see it). But I think if you get the mass-assignment error you'll do it anyway :)
I hope it helps.

Related

simple_form with two models?

This is my creation form for Customer model.
While populating customers table I am inserting some data in managers table as well. But I want to add a date picker in this simple_form but that date is only stored in Manger model and Customer model doesn't have date field. How do I do it? What alternative options I have?
new.html.erb
<%= stylesheet_link_tag "customers" %>
<div class="row">
<div class="panel panel-default center" id="new-width">
<div class="panel-body">
<%= simple_form_for #customer do |f| %>
<%= f.input :name,:autocomplete => :off %>
<%= f.input :principalAmount,:autocomplete => :off %>
<%= f.input :interestRate %>
<%= f.input :accountType %>
<%= f.input :duration,:autocomplete => :off %>
<%= f.button :submit %>
<% end %>
</div>
</div>
</div>
Edit: Manager model has many field which is independent of Customer model. But When a customer is created it has to add a date in Manager model which is absent in the Customer model.
I suggest you to use accepts_nested_attributes_for in customers model.
Something like this:
In customer model,
accepts_nested_attributes_for :managers
In view page,inside the existing form
<%= f.fields_for :managers do |m| %>
<%= m.date_field :date %>
<% end %>
You can always add a getter and setter to the customer model and manually set the manager fields. Again it depends on the relationship with manager, if it exists already, etc. but the main point is you can create methods that can then be accessed in the form as customer methods.
# in customer.rb
def manager_date=(date)
manager.date = date
end
def manager_date
manager.date
end
then in the form
<%= f.input :manager_date %>
Note - this is a brief example, you'll need to save the manager somewhere and doing this before or after the customer is updated will depend on your needs.
Another way to do this is to create an attr_accessor for manager_date in customer and if it's there, update the manager after the customer is saved
after_save :update_manager
def update_manager
manager.date = manager_date
manager.save
end

Ruby on Rails multiple images connected to one object

I've been trying to create a form that would get parameters for multiple models. I have a photo model that belongs to a product model and I want to make it so that when you create a new product you can also upload images that are linked to that product by id.
<%= form_for #product, html:{multipart:true} do |f| %>
<div class="field">
<%= f.label :price %>
<%= f.text_field :price %>
</div>
<%=form_for #photo do |t| %>
<%t.productID = f.id%>
<div class="field">
<%= t.label (:image) %>
<%= t.file_field (:image) %>
</div>
<%end%>
<div class="actions">
<%= f.submit %>
</div>
<%end%>
right now I'm using paperclip for image attachments and the photo model accepts the images as parameters. I've used paperclip before but the product could only have one image connected to it. If I use the form above I get "First argument in form cannot contain nil or be empty" error and it points to where the form_for #photo starts.I have controllers for both with the usual methods of new, create, update, etc. I've routed resources to both product and photos but I'm still pretty new to rails and don't fully understand how this stuff works.
I think what you're trying to do is a good application for nested forms using the fields_for helper.
First, you'll need to ensure that your product model and photo model have the right associations (A product probably has_many photos, and a photo belongs to a product, right?). Then you'll make sure the product class 'accepts nested attributes for photo's which allows you to add attributes to the photos model from a products form.
in products.rb
class Product
has_many :photos
accepts_nested_attributes_for :photos
end
and in photo.rb
class Photo
belongs_to :product
end
Then you'll want to make sure any attributes you need for the photo are white-listed in your product params.
in products_controller.rb
private
def product_params
params.require(product).permit(:first_product_attribute, :second_produtc_attribute, photo_attributes: [:image])
end
Last, you'll create the form using the special helper fields_for
in your view
<%= form_for #product, html:{multipart:true} do |f| %>
<div class="field">
<%= f.label :price %>
<%= f.text_field :price %>
</div>
<%= f.fields_for :photo do |t| %>
<div>
<%= t.label :image %>
<%= t.file_field :image, :multiple => true %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<%end%>
You'll also need to make sure you're actually creating new photo objects in your product's create action:
in products_controller.rb
def create
#product = Product.new(product_params)
if #product.save!
params[:photo]['image'].each do |img|
#photo = #product.photos.create!(:image => img)
end
flash[:success] = 'product saved!'
redirect_to #product
end
end
Some of this is based on my experience doing the same thing but with Carrierwave instead of Paperclip so your specific implementation might be a little different.
I dont think this is a proper method <%t.productID = f.id%>. Maybe try <% t.text_field :productID, value = f.id, type = hidden %> or something along those lines?
heres some docs for the form helper so you know what to put after t.abcd
http://apidock.com/rails/v3.2.3/ActionView/Helpers/FormHelper/form_for
You're getting the
"First argument in form cannot contain nil or be empty"
..error because #photo is nil, you need to set it in your controller #photo = Photo.new.
Also, form tags inside form tags are invalid HTML.
https://www.w3.org/TR/html5/forms.html#the-form-element
Forms
Content model: Flow content, but with no form element
descendants.
You want to use f.fields_for instead. Learn how to use it here here
I have controllers for both with the usual methods of new, create,
update, etc.
You only ever hit one controller and action when you go to a path, say /photos will only hit the photos controller (as configured in your routes.rb). This I think is where you're messing up the #photo variable. Set both in the same controller in order for the view to be able to see both variables.

Rails 4 form to set has_many through additional column

I have a has_many association between Items and their Components through a table called ComponentItems. ComponentItems contains a column quantity in addition to item_id and component_id. How is it possible to add a number_field to my form that shows the quantity of each component required for an item? The form must contain a number_field for each Item in the database, even if no relationship exists (i.e. #item.component_ids.empty? == true).
class Item < ActiveRecord::Base
has_many :components, through: :component_items
has_many :component_items
end
class Component < Item
has_many :items, through: :component_items
has_many :component_items
end
class ComponentItem < ActiveRecord::Base
belongs_to :item
belongs_to :component
end
I believe I've tried every permutation of model, controller and form_builder possible, except the correct one.
In response to the answer below, here's a form that shows a checkbox and the item code for component items that make up one particular item;
<%= form_for [#item] do |f| %>
<%= f.collection_check_boxes :component_items, Item.active.where.not(sku: #item.sku).sort_by{|n| n.sku}, :id, :sku do |b| %>
<%= b.check_box %> <%= b.label %><br/>
<% end %>
<% end %>
So, ideally I'd replace the check_box with a number_field for quantity. How?
So it seems what I wanted is not so straightforward after all. In the end I opted for using some jQuery for adding extra Components to Items via a separate form. Trying to add/remove components and adjust the quantities was beyond me, so choosing to use separate forms for each user action seemed simpler. It may not be the most user-friendly way of working but it's the best I have.
To edit the quantities I did the following;
<% #item.component_items.each do |x| %>
<%= hidden_field_tag "item[component_items_attributes][][id]", x.id%>
<%= label_tag x.component.sku, x.component.sku.upcase, :class=>"col-md-3 control-label" %>
<%= number_field_tag "item[component_items_attributes][][quantity]", x.quantity, :class=>"col-md-5"%>
<%end %>
and ensured the Item model accepted nested attributes for component_items. Finally, add the nested params array for multiple component_items to items_controller.rb...
def item_params
params.require(:item).permit(
:component_items_attributes =>[:component_id, :item_id, :quantity, :id]
)
end
Note I didn't use fields_for which seemed to generate an extra component_items_attributes array that didn't make any sense at all.
This should work:
#item.components.to_a.sum(&:quantity)
This will throw an error if quantity on some component is nil, so you may try like this to avoid errors:
#item.components.to_a.map(&:quantity).compact.sum
UPDATE
<% #item.component_items.each do |component_item| %>
<%= form_for(component_item) do |f| %>
<div class="field">
<%= f.label :quantity, 'Quantity' %><br />
<%= f.number_field :quantity %>
</div>
<% end %>
<% end %>

Accessing different model elements in Rails

I am inexperienced in Rails (version 4.2.5) and struggle with how views access database elements. I have worked through a number of different tutorials but still don't really understand why it doesn't work the way I think it does!
I have models that have been set up with references which I believe that establishes foreign keys in the database. I want to edit entries in the database that belong in a different model.
So, a Wines is a model that references Winemakers.
class Wine < ActiveRecord::Base
belongs_to :winemaker
end
In my _edit_form.html.erb file I have the following code which works but does not give me what I want:
<%= simple_form_for(#wine) do |f| %>
<div class="field">
<%= f.label :winemaker_id %>
<%= f.text_field :winemaker_id %>
</div>
This produces a simple box and in the box the integer that is winemaker_id is displayed but what I want is the actual name of the winemaker. I have tried :winemaker_id.name, #winemaker.name and many variations on those theme but I clearly do not understand how this works. I have tried reading various documentation but I am none the wiser.
Can someone please explain in simple terms how accessing different models works?
If your Winemaker model has been defined as follows:
class Winemaker < ActiveRecord::Base
has_many :wines
end
That means you can write the followings:
#winemaker.wines - returns all the wines belongs to a winemaker
#wine.winemaker - returns the winemaker to whom the wine belongs
If you want to show and edit the Winemaker name from Wine form, then you can do it using accepts_nested_attributes_for
Just modify your Wine model as follows:
class Wine < ActiveRecord::Base
belongs_to :winemaker
accepts_nested_attributes_for :winemaker
end
Now you can make a small change to your form as follows:
<%= form_for #wine do |f| %>
<%= f.fields_for :winemaker do |w|%>
<%= w.text_field :name%>
<% end %>
<%= f.submit%>
<% end %>
Try the following code:
<%= simple_form_for(#wine) do |f| %>
<div class="field">
<%= f.label :winemaker_id %>
<%= f.collection_select(:winemaker_id, Winemaker.all, :id, :name) %>
</div>
Have a look at http://apidock.com/rails/ActionView/Helpers/FormOptionsHelper/collection_select for more information.

Ruby on Rails: Multiple Input Fields in The Same Form - Change ID/Value

Have a page where there are multiple input fields of the same thing, Posts. Right now, when a user enters in a question for, let's say 3 fields, the only one that saves to the database is the last one. Whereas, it should save all three and give them each it's own post_id. Also; if the user doesn't enter anything in for the other fields, it should not save in the database either.
new_step_4_html.erb
<%= form_for(#post) do |f| %>
<%= f.text_field :content %>
<%= f.text_field :content %>
<%= f.text_field :content %>
<% end %>
projects_controller.rb
def new_step_4
#post = Post.new
end
Right now, all it does is submit one :content field, obviously because they all share the same id/value. Unfortunately, the Railscasts #197 applies for nested forms, so the javascript and helper stuff he does all applies for nested. I would think this is something simple. Person from IRC mentioned I could do some sort of '3.times' code into the view file or something?
First of all you will probably have to edit the model of you post.
post.rb
has_many :contents, :dependent => :destroy
accepts_nested_attributes_for :contents
You will need another model to store the content fields.
so first generate a model
rails g model content post_id:integer body:text
the model
content.rb
belongs_to :post
Now, in stead of doing <%= f.text_field :content %> a few times, let rails create them, because now you basically let them overwrite each other.
3.times do
content = #post.content.build
end
the form view will be something like this:
<%= form_for #post do |f| %>
<%= f.fields_for :contents do |builder| %>
<%= builder.label :body, "Question" %><br />
<%= builder.text_area :body, :rows => 3 %><br />
<%= end %>
<p><%= f.submit "Submit" %></p>
<% end %>
I did not test this code, but the idea should be correct. Let me know if you need more info.

Resources