Rails loop showing duplicate results - ruby-on-rails

I have three models with the following associations:
class Product < ApplicationRecord
belongs_to :store
has_many :variants, dependent: :destroy
end
class Store < ApplicationRecord
has_many :variants
belongs_to :user
has_many :products, dependent: :destroy
end
class Variant < ApplicationRecord
belongs_to :product
belongs_to :store, optional: true
end
And I'm counting the products & variants of each user with the following:
#products = current_user.store.products.group(:subcategory_id).count
#variants = current_user.store.variants.group(:subcategory_id).count
The above returns this #products => {181=>1, 185=>1}
and this #variants => {181=>2, 185=>1}
Finally when I try to loop through each of the above hashes the results show up correctly but they also show up two times(dublicate results). Any ideas on how to fix this??
<% #products.each do |product_key, product_value| %>
<% #variants.each do |variant_key, variant_value| %>
<%= #child_category.name %> - <%= #subcategory.name %><br>
Products: <%= product_value %><br>
Variants: <%= variant_value %><br>
Total Products: <%= product_value + variant_value %><br>
<% end %>
<% end %>
This is the result:

This is not duplicating anything, this is expected behavior. It shows each product with its variants.
You're looping through two hashes, which contains 2 pairs, so the outer loop executes 2 times and the inner loop executes 2 * 2 => 4 times.
This is the expected behavior.
And if you wanted to show only a single pair, you can add if condition over there for subcategory -
i.e. I wanted to show only results where subcategory is Boys then you can use following code -
<% #products.each do |product_key, product_value| %>
<% #variants.each do |variant_key, variant_value| %>
<% if #subcategory.name == 'Boys' %>
<%= #child_category.name %> - <%= #subcategory.name %><be>
Products: <%= product_value %><be>
Variants: <%= variant_value %><be>
Total Products: <%= product_value + variant_value %><be>
<% end %>
<% end %>
<% end %>

Related

Rails many to many relations with has many through relationships

I have the following models using the has_many :through relationship.
A recipe can have many seasons.
A season can have many recipes.
Recipe
class Recipe < ApplicationRecord
attribute :name
has_many :recipe_seasons
has_many :seasons, through: :recipe_seasons
end
Season
class Season < ApplicationRecord
has_many :recipe_seasons
has_many :recipes, through: :recipe_seasons
end
Recipe Season
class RecipeSeason < ApplicationRecord
belongs_to :recipe
belongs_to :season
end
I'm currently displaying all recipes on the index page using the the following
Controller
def index
#recipes = Recipe.all
render index: #recipes, include: [:recipe_seasons, :seasons]
end
View
<% if #recipes.present? %>
<% #recipes.each do |recipe| %>
<%= link_to recipe.name,[recipe] %>
<% end %>
What I want to do is to have the seasons displayed with the each recipe. A recipe can have more than one season and so I added another for loop inside the existing one for recipes.
I have so far tried:
<% #recipes.each do |recipe| %>
<% recipe.seasons.each do |season| %>
<%= link_to recipe.name,[recipe] %>
<%= season.name %>
<% end %>
<% end %>
<% end %>
Current Behaviour
Recipe 1 - Season 1
Recipe 1 - Season 2
Expected Behaviour
Recipe 1 - Season 1, Season 2
Recipe 2 - Season 4
You must include the seasons in the body parameter of the link_to (the text displayed in the link)
<% #recipes.each do |recipe| %>
<%= link_to "#{recipe.name} - #{recipe.seasons.map(&:name).join(', ')}", [recipe] %>
<% end %>

Join Table Quantity

I'm learning Rails building an ordering system and I'm stuck trying to build a form for Orders. My models look like this:
class Restaurant < ActiveRecord::Base
has_many :orders
end
class Order < ActiveRecord::Base
belongs_to :restaurant
has_many :order_recipes, dependent: :destroy
has_many :recipes, through: :order_recipes
end
class Recipe < ActiveRecord::Base
belongs_to :restaurant
has_many :order_recipes
has_many :orders, through: :order_recipes
end
I used to handle the input for Recipes using checkboxes, which only allowed me to add one of each recipe:
<%= form_for([#restaurant, #order]) do |f| %>
<%= f.label :Table_Number %>
<%= f.number_field :table_id %>
<strong>Recipes: </strong>
<br>
<%= f.collection_check_boxes :recipe_ids, #recipes, :id, :name do |cb| %>
<% cb.label(class: "checkbox-inline input_checkbox") {cb.check_box(class: "checkbox") + cb.text} %>
<% end %>
<%= f.collection_select(:recipe_ids, #recipes, :id, :name) %>
<%= f.submit(#order.new_record? ? "Create Order" : "Edit Order", class: "btn btn-success") %>
<% end %>
But now I need to also handle quantities. So I added the "quantity" column to the order_recipes table. And I can't figure out how to build the proper form to be able to submit an Order object, with an order_recipe array of objects containing [recipe_id, order_id, quantity] per row. I'm also opened to using formtastic if it makes things easier, although I'm not very good with it yet.
ETA: Since the quantity field is on the orders_recipes table, you'll want to create an #orders_recipes object with all the correct recipe_ids instead:
#orders_recipes = #recipes.map{|r| #order.order_recipes.build(recipe: r)}
Then you can use the FormHelper's fields_for method:
<%= f.fields_for :order_recipes, #order_recipes do |orf| %>
<%= orf.hidden_field :recipe_id %>
Quantity: <%= orf.number_field :quantity %>
<%- end -%>
If desired, you'll need to "manually" remove order_recipes with nil or 0 values for quantity.
For this to work, you need an accepts_nested_attributes_for in your model, like so:
class Order < ActiveRecord::Base
…
accepts_nested_attributes_for :order_recipes
end

Rails Forms Passing Array in Params for M to M Associaton

Edit: Essentially looking to pass something like this:
{
'tabled_id' : '1',
'recipes' : [{
{ 'recipe_id' : '3',
'quantity' : '2'
}
{ 'recipe_id' : '5',
'quantity' : '1'
}
}]
}
And I think I should do params.require(:order).permit(:table_id, {recipes:, [:id,:quantity]} ) on the controller side.
I'm learning Rails building an ordering system and I'm stuck trying to build a form for Orders that passes quantity. Where Orders is a nested resource for Restaurant.
My models look like this:
class Restaurant < ActiveRecord::Base
has_many :orders
has_many :recipes, dependent: :destroy
end
class Order < ActiveRecord::Base
belongs_to :restaurant
has_many :order_recipes, dependent: :destroy
has_many :recipes, through: :order_recipes
end
class Recipe < ActiveRecord::Base
belongs_to :restaurant
has_many :order_recipes
has_many :orders, through: :order_recipes
end
View:
<%= form_for([#restaurant, #order]) do |order_form| %>
<%= order_form.label :Table_Number %>
<%= order_form.number_field :table_id %>
<h3>Recipes: </h3>
<br>
<% #restaurant.recipes.each do |recipe| %>
<%= order_form.fields_for :recipe, recipe do |r| %>
<%= r.label recipe.name %>
<%= r.hidden_field :id %>
<%= r.number_field :quantity %>
<% end %>
<% end %>
<%= order_form.submit(#order.new_record? ? "Create Order" : "Edit Order", class: "btn btn-success") %>
<% end %>
This will yield a form that looks correct, but won't pass all parameters. Let's say I have 3 recipes. And I set their quantities to 2,3,4 respectively, and the table_id to 1. When I inspect the parameters, I see that only the last recipe with its quantity has been passed. params[:order] => {"table_id"=>"1", "recipe"=>{"id"=>"4", "quantity"=>"4"}} I need to be able to send all recipes with their assigned quantities. Also, I'm using the accepted answer in this question to be able to access the quantity column: Rails 4 Accessing Join Table Attributes
When you hand in fields_for :recipes multiple times, the fields_for method is not aware of you sending an array of things. Therefore it will name the parameters as if it was only one instance, so only the last instance will come through. You have to hand in the array of recipes to the fields_for, so it can name the parameters, so that rails knows it is an array of things when it gets picked up again (docs).
This is because form parameters in browsers do not support nesting by default. The actual parameters are flat key-value paramters. Rails has some naming conventions on how paramters can be named, so they will automatically be coerced to an array.
<%= form_for([#restaurant, #order]) do |order_form| %>
<%= order_form.label :Table_Number %>
<%= order_form.number_field :table_id %>
<h3>Recipes: </h3>
<br>
<%= order_form.fields_for :recipes, #restaurant.recipes do |r| %>
<%= r.label recipe.name %>
<%= r.hidden_field :id %>
<%= r.number_field :quantity %>
<% end %>
<%= order_form.submit(#order.new_record? ? "Create Order" : "Edit Order", class: "btn btn-success") %>
<% end %>

Paper trail display has many objects

I'm using the paper trail gem to track version changes on order web application. I'm having trouble displaying the has_many versions
Order model:
class Order < ActiveRecord::Base
has_paper_trail
has_many :line_items, dependent: :destroy, inverse_of: :order, order: "position", autosave: true
end
line item model:
class LineItem < ActiveRecord::Base
belongs_to :order, inverse_of: :line_items
has_paper_trail
end
Order Controller:
#order = Order.find(params[:id])
#versions = PaperTrail::Version.where(item_id: params[:id]).order('created_at ASC')
#line_items = LineItem.find_all_by_order_id(params[:id])
#line_item_versions = #line_items.versions
history html:
<% #line_item_versions.each_with_index do |version, index| %>
<b>Version: </b><%= index + 1 %><br/>
Event ID: <%= version.id %><br/>
<b>Target:</b> <%= version.item_type %>
<small>(id: <%= version.item_id %>)</small>; <b>action</b> <%= version.event %>;<br/>
<% end %>
The problem is the .versions works when a single object (from .find) is found. However, when an array of objects (from the .find_all_by) is passed to the .versions it returns this error
undefined method `versions' for #<Array:0x007f859d37eb30>
You are defining:
#line_items = LineItem.find_all_by_order_id(params[:id])
Which means #line_items is an Array of several LineItem records. Then, you call:
#line_item_versions = #line_items.versions
But the .versions method is an instance method of LineItem (one line_item has_many versions). This causes the error undefined method 'versions' for Array (#line_items is an array here).
To solve this problem,
I think you should do the following (but there is many options, depending on what you want to do):
<% #line_items.each do |line_item| %>
<%= line_item.name %>
<% line_item.versions.each_with_index do |version, index| %>
<b>Version: </b><%= index + 1 %><br/>
Event ID: <%= version.id %><br/>
<b>Target:</b> <%= version.item_type %>
<small>(id: <%= version.item_id %>)</small>; <b>action</b> <%= version.event %>;<br/>
<% end %>
<% end %>

Sum 2 columns a value with array

Hello everybody.
I'm trying to sum 2 columns(amount_ensure and vehicle_Value) from different tables in my view
TABLES
|POLICIES|
|id| |amount_ensure|
|POLICY_VEHICLES|
|id| |policy_id|
|VEHICLES|
|Id| |vehicle_value|
Here is my controller
def view_policy
#obj_policy = Policy.find(params[:id])
end
Here is my model
class Policy < ActiveRecord::Base
has_many :policy_vehicles
has_many :vehicles, :through => :policy_vehicles
end
class PolicyVehicle < ActiveRecord::Base
belongs_to :vehicle
belongs_to :policy
end
class Vehicle < ActiveRecord::Base
belongs_to :policy
has_many :policy_vehicles
has_many :policies, :through => :policy_vehicles
end
Here is my view when in my partial view #obj_policy.vehicle is empty show only amount_ensure but when has value do SUM (but is an array from my partial view)
<% if #obj_policy.vehicles.empty? %>
Sum:
<%= #obj_policy.amount_ensure %>
<% else %>
Sum:
<%= #obj_policy.amount_ensure + #obj_policy.vehicles.vehicle_value.to_i %>
<% end %>
<%= render :partial=>"vehicles" %>
My partial view
<% #obj_policy.vehicles.each do |vehicle| %>
<%= vehicle.vehicle_value %>
<% end %>
How can i fix this problem?
I will appreciate help.
This should work
<% else %>
Sum:
<%= #obj_policy.amount_ensure + #obj_policy.vehicles.collect(&:vehicle_value).sum %>
<% end %>
#obj_policy.vehicles.collect(&:vehicle_value).sum will be 0 when vehicles array is empty
Actually it will be better to allow SQL to handle it.
<%= #obj_policy.amount_ensure + #obj_policy.vehicles.sum(:vehicle_value) %>
You can also wrap it into a instance method:
class Policy
def total_value
amount_ensure + vehicles.sum(:vehicle_value) # I don't remember if `to_i` is necessary here in case of empty collection, try it out
end
end
It's also possible to write a custom SQL query that will do this summing in database. In case of original solution proposed it fetches ALL vehicles with ALL fields that might not be necessary at all. Try to use SQL as much as possible for aggregation jobs.

Resources