Ruby - Nesting objects through input values - ruby

Hello my fellow companion!
What I'm trying to achieve is a system by which an Order form is compiled in two ways:
by fullfilling its own attributes (:sender_name, :sender_mobile etc..)
by selecting products through the price labels attached on them.
After a while of poking here and there, I managed to display the product list on the order form. Here the 3 models and the views
models/order.rb
class Order < ActiveRecord::Base
attr_accessible :sender_comment, :sender_email, :sender_mobile, :sender_name, :order_attributes
has_many :products
accepts_nested_attributes_for :products
end
models/product.rb
class Product < ActiveRecord::Base
belongs_to :order
attr_accessible :product_name, :product_description, :prices_attributes, :order_id
has_many :prices
accepts_nested_attributes_for :prices
end
models/price.rb
class Price < ActiveRecord::Base
belongs_to :product
attr_accessible :product_id, :price_label, :price_amount, :price_checked, :how_many_prices, :products_attributes
end
views/orders/_form.html.erb
<%= form_for(#order) do |f| %>
<div class="field">
<%= f.label :sender_name %><br />
<%= f.text_field :sender_name %>
</div>
# [...] other order's fields...
<%= f.fields_for :product do |builder| %>
<%= render "products_field", :f => builder %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
views/orders/_products.html.erb
<% #products.each do |p| %>
<td><%= p.product_name %></td>
<td><%= p.product_description %></td><br />
<% p.prices.each do |price| %>
<td><%= price.price_label %></td><br />
<td><%= price.price_amount %></td><br />
<td><input type="radio" class="order_bool" name="<%= p.product_name %>" <% if price.price_checked == true; puts "SELECTED"; end %> value="<%= price.price_amount%>"/></td><br />
<% end %>
<% end %>
Products and relatives prices are printed in the Order form, yet once selected they're not saved as order_attributes; along with the order's attributes, every radio selected generates an object like this
[#<Product id: nil, order_id: 23, product_name: nil, product_description: nil, created_at: nil, updated_at: nil>]
How can I convert the selected products into effective order_attributes?
This is my first project with OOP and that I'm learning all by myself, with a very few help but from the internet. Please don't be too harsh!
Also feel free to change the title if you don't consider it appropriate enough; english is not my native language and i find very difficult to recap this issue in just a few words.
Thanks for the patient :)

Basically your current _products_fields partial isn't even attached to the form object. You're building arbitrary inputs right now. See form_for documentation and fields_for documentation and radio_button documentation.
Note: When you use f.method_name you're actually going to call the FormBuilder versions of the method and not the FormHelper versions. Since the FormHelper version has more and still relevant documentation that's the better version. Just omit the object_name parameter.
I think changing a these lines should do the trick:
Form Partial:
<%= f.fields_for :products, #products do |builder| %>
<%= render "products_field", :f => builder %>
<% end %>
Products Fields Partial
# 'f' is the variable passed in using ':f => builder'
# So 'f' = builder
# f.object is accessing the object were building the fields for
<td><%= f.object.product_name %></td>
<td><%= f.object.product_description %></td><br />
<% f.object.prices.each do |price| %>
<td><%= price.price_label %></td><br />
<td><%= price.price_amount %></td><br />
<td><%= f.radio_box("price", price.price_amount %></td><br />
<% end %>

Related

How to save many items on one form rails?

I need to save many items to Cart on form, user enter quantity one form, and selected items goes to db, but now save only first entered quantity of item. Why?
my form
<%= form_for #cart_item do |f| %>
<% #category.items.each do |item| %>
<%= item.name %>
<%= f.hidden_field :item_id, :value => item.id %>
<%= f.text_field :qty %>
<% end %>
<%= f.submit %>
<% end %>
And controller
cart_items_controller.rb
class CartItemsController < ApplicationController
before_action :set_cart, only: [:create]
def create
#cart_items = CartItem.create(cart_items_params)
#cart_items.cart_id = #cart.id
if #cart_items.save
redirect_to :back
else
render root_path
end
end
private
def cart_items_params
params.require(:cart_item).permit(:id, :qty, :item_id, :cart_id)
end
def set_cart
#cart = Cart.find(session[:cart_id])
rescue ActiveRecord::RecordNotFound
#cart = Cart.create
session[:cart_id] = #cart.id
end
end
There are a few problems here. I'll give you a little bump:
<% #category.items.each do |item| %>
<%= item.name %>
<%= f.hidden_field :item_id, :value => item.id %>
<%= f.text_field :qty %>
<% end %>
For each CartItem, this is going to create an input like this
<input name="qty">
This is problematic because only one (the last one in the DOM) will be submitted. You need to research fields_for and incorporate that into your loop in order to get unique names for each Item in the form.
This same issue follows through into your controller
def cart_items_params
params.require(:cart_item).permit(:id, :qty, :item_id, :cart_id)
end
This is going to look for a single :id, :qty, :item_id, and :cart_id, when in reality you're looking to accept multiple :item_id and :qty fields. You need to research Strong Parameters with nested has_many associations.
Finally you have this
#cart_items = CartItem.create(cart_items_params)
which is going to attempt to create a single CartItem when you're really trying to create multiple items and associate them back to the Cart. You need to research accepts_nested_attributes_for as well as more generally "rails form save has_many association". It's a widely covered topic here on SO and elsewhere.
I do this:
def create
#cart_items = params[:cart_items]
#cart_items.each do |c|
#cart_item = CartItem.new(c)
if #cart_item.qty.present?
#cart_item.cart_id = #cart.id
#cart_item.save
end
end
and form
<%= form_tag cart_items_path do %>
<% #cart_items.each do |cart_item| %>
<%= fields_for "cart_items[]", cart_item do |f| %>
<% #category.items.each do |item| %>
<%= item.name %>
<%= f.hidden_field :item_id, value: item.id %>
<%= f.text_field :qty %>
<% end %>
<%= f.submit %>
<% end %>
<% end %>
<% end %>

how to do a many to many model for this project in rails

I'm having trouble creating a many to many model for my project.
Basically i have a Matches & Teams model.
Teams are created prior to the Matches.
Once the match is created then i would like to add teams to it.
Match can have many teams, Teams can have many matches.
I'm currently adding teams via nested_form and adding multiple teams at once.
When submitting the form, i get an error expecting the team to be in a relationship already with the match.
I can do this with a many to one relationship but it fails with many-to-many, was wondering if there was any way to do it without doing a custom route.
Below is the form, controllers are as per default values.
Form:
<%= nested_form_for(#match) do |f| %>
<% if #match.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#match.errors.count, "error") %> prohibited this match from being saved:</h2>
<ul>
<% #match.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :date %><br />
<%= f.date_select :date %>
</div>
<%= f.fields_for :teams, :html => { :class => 'form-vertical' } do |builder| %>
<%= builder.label "Team Name:" %>
<%= builder.autocomplete_field :name, autocomplete_team_name_teams_path, :update_elements => {:id => "##{form_tag_id(builder.object_name, :id)}" },:class => "input-small",:placeholder => "Search" %>
<%= builder.hidden_field :id %>
<% end %>
<%= f.link_to_add raw('<i class="icon-plus-sign"></i>'), :teams, :class => 'btn btn-small btn-primary' %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Using a join model, the has_many :through macro and the accepts_nested_attributes_for macro you can do something like the following.
class Match
has_many :competitions
has_many :teams, :through => :competitions
accepts_nested_attributes_for :teams
end
class Competition
belongs_to :match
belongs_to :team
end
class Team
has_many :competitions
has_many :matches, :through => :competitions
end
Just make sure your form is set up to send the following data structure as params when the request reaches the create or update controller.
params => {
:match => {
# ...
:teams_attributes => [
{ :name => 'Foo', :color => 'blue' },
{ :name => 'Bar', :color => 'green' },
# ...
]
}
}

Adding comment as "most commented" on my view?

I have a Rails 3 blog, which has Article and Comment models in has_many and belongs_to associations. When an article comment is > 3, I should see it in my application.html.erb views so I can call it "most commented".
<div class="span2">
<%=image_tag ("Lekki_Gardens_new.gif") %>
<br><br>
<b><p>News update</p></b>
<% #articles.first(4).each do |article| %>
<%=image_tag article.avatar_url(:lilthumb).to_s, :class=>"img-polaroid" %><br>
<%= link_to article.name, article%><hr>
<% end %>
</div
You can use the :counter_cache option in your article model and then use a scope to retrieve the most commented ones.
class Article < ActiveRecord::Base
has_many :comments, counter_cache: true
scope :most_commented, where('comments_count > 3')
end
And then in your template:
<% Article.most_commented.each do |article| %>
<% # anything you want %>
<% end %>

Rails Save File Checkbox

How can I make it so that my info only gets stored if the checkbox is checked. Here is what I have so far:
<% #extra.each do |extra| %>
<%= f.fields_for :purchaseds do |builder| %>
<div class="label-field">
<%= builder.label :name, extra.name %>
<p><%= extra.description %></p>
</div>
<div class="text-field">
$<%= extra.price %>
<%= builder.check_box :purchased %>
</div>
#I WOULD LIKE THIS TO ONLY GET SAVED IF THE CHECK BOX FOR PURCHASED IS CHECKED
<%= builder.hidden_field :name, :value => extra.name %>
<%= builder.hidden_field :description, :value => extra.description %>
<%= builder.hidden_field :price, :value => extra.price %>
<% end %>
<% end %>
My client asked to be able to add extra services himself, and then users could be able to choose if they want to purchase them as accessories to their order. So what I did was I made a table called Extra (for extra services) and another table called Purchased. Purchased belongs to Order and is a nested attribute.
In your purchaseds model add validations:
validates_presence_of :name, :description, :price, :if => :purchased
Update
Add :reject_if option to your purchaseds parent model in
accepts_nested_attributes_for :purchaseds, :reject_if => {|attrs| !attrs[:purchased]}

Rails 3 Nested Form not being created

The models I'm working with look like this:
class ComplexAssertion < ActiveRecord::Base
has_many :expression_groups
has_many :expressions, :through => :expression_group
accepts_nested_attributes_for :expression_groups, :allow_destroy=>true
end
class ExpressionGroup < ActiveRecord::Base
belongs_to :complex_assertion
has_many :expressions
accepts_nested_attributes_for :expressions, :allow_destroy=>true
end
class Expression < ActiveRecord::Base
belongs_to :expression_group
end
My form looks like the following:
<%= form_for(#complex_assertion) do |f| %>
<div id="mainAssertionGroup" style="border:1px; border-style:solid; width:1000px; padding:5px">
<div class="field">
<%= f.label :title %>: <%= f.text_field :title, :size=>'10' %>
<%= f.label :description %>: <%= f.text_field :description, :size=>'25' %>
<%= f.label :scope %>: <%= f.text_field :scope, :size=>'1' %>
Test
Category: <%= collection_select(:complex_assertion, :assertion_category_id, AssertionCategory.all, :id, :name, {:include_blank=>"UNCATEGORIZED"}) %>
</div>
<div id="initialGroup" style="border:1px; margin-left:10px; margin-top:10px; border-style:solid; width:850px;">
<div class="childGroup1" style="padding:5px;">
<%= f.fields_for :expression_groups do |eg| %>
<%= eg.fields_for :expressions do |e| %>
Type: <%= e.collection_select :assertion_type_id, AssertionType.all, :id, :name %>
Attribute: <%= e.collection_select :attribute_name, Attribute.find_by_sql("select distinct a.name from attributes a "), :name, :name %>
<%= e.label :operator_type_id %>
: <%= e.collection_select :operator_type_id, OperatorType.all, :id, :value %>
Value: <%= e.text_field :value, :size=>'1' %>
<% end %>
<div id="innerOperator">
<%= eg.collection_select :logical_operator_type_id, LogicalOperatorType.all, :id, :value %>
</div>
<% end %>
</div>
</div>
</div>
<div id="createComplex" align="center">
<%= f.submit :value=>'Submit' %>
</div>
<% end %>
And my controller looks like:
def new_complex_assertion
#complex_assertion = ComplexAssertion.new
end
When I load the page, I only the ComplexAssertion portion of the form and get nothing back for the ExpressionGroups or the Expressions. It's as if there isn't anything available. But, if you see my controller, I did a ComplexAssertion.new which I though would create the dependent objects automagically; I assume I'm incorrect?
I'm debugging through RubyMine and when I evaluate the ComplexAssertion.new, I only see 5 attributes, the five that are defined for only that object, none of the relational objects. What am I doing incorrectly?
EDIT
Looks like if I do the following:
#complex_assertion = ComplexAssertion.new
#complex_assertion.expression_groups.build
#complex_assertion.expressions.build
And change my form to use:
<%= f.fields_for :expressions do |e| %>
instead of eg.fields_for, it shows the forms.
This DOES NOT give me the correct nesting. I thought I should be able to do:
#complex_assertion.expression_groups.expressions.build
but it tells me that expressions is an undefined method.
Yes, you have to explicitly instantiate the associated objects. It is not done for you.
#complex_assertion.expression_groups.expressions.build
Will not work because expression_groups is an array and not an individual expression group. So, after you create the expressions_groups do the following:
#complex_assertion.expressions_groups.each do |group|
group.expressions.build
end
Also, you could replace the 2nd line with the following as well to create multiple expressions
2.times do { group.expressions.build }
As for using fields_for with nested models, make your code in the form look like this:
<%= f.fields_for :expression_groups, #complex_assertions.expression groups do |eg| %>
<%= eg.fields_for :expressions, eg.object.expressions do |e| %>
I will try to explain what is going on. The :expressions_groups is telling fields_for what class of object it is going to render fields for, and the second part I added is telling fields_for where to find the object(s) to render fields for. If we are passing in an array, which we are in this case, it will automatically iterate over the array. On each iteration, it puts the current model object we are working with into a variable called object which is stored in the form builder instance returned by fields_for. So we use this to tell the second fields_for where to find the expression model objects it needed. This means eg.object points to an expression_group model object.
I hope this helps and makes sense. Also, I have not tested anything and am only pointing out what looks out of place.

Resources