I have a order form with restrictions on the number of items you can select that accepts nested attributes and I'd like to perform a create and an update on it's associated items when I update the order.
My form looks like:
<%= simple_form_for #food_order do |f| %>
<% #food_order.order_items.each do |oi| %>
<%= f.fields_for :order_items, oi do |oi_form| %>
<%= oi_form.input :quantity %>
<% end -%>
<% end -%>
<% end -%>
My validator looks like:
# OrderItem.rb
validate :food_order_quantity
def food_order_quantity
if (order.limit - order.order_items.sum(:quantity)) < self.quantity
errors.add(:base, "Too many products. Please update your cart.")
end
end
Let's imagine I create an order with a limit of 10 items and select 10 items:
food_order = FoodOrder.create(limit: 10)
order_item_1 = OrderItem.create(order: food_order, quantity: 10)
If I attempt to update the order by reducing order_item_1's quantity by 1 and adding a new order_item with a quantity of 1 I get an error even though the total quantity is correct:
order_item_1.quantity = 9
order_item_2 = OrderItem.new(order: food_order, quantity: 1)
put client_food_order_path(#client, food_order), params: {
food_order: {
id: food_order.id,
order_items_attributes: [
order_item_1.attributes,
order_item_2.attributes,
]
}
}
# returns the following error
#messages={:"order_items.base"=>["Too many products. Please update your cart."]}
I understand that it's attempting to save order_item_2 before updating order_item_1 and in that time, the controller believes there are 11 items (simply because order_item_1 has not yet been updated).
What can I do to allow this sort of operation?
Related
i'm trying create a record filter on their associations. So I have creatives and they have multiple talents. I want to have av view that filters the creatives with a specific talent. But still display each creatives multiple talents in the view.
class Creative
has_and_belongs_to_many :talents
end
Creative -> HABTM -> Talents
#creatives = Creative.includes(:talents, user: [:profile_image_attachment])
#creatives = #creatives.where(talents: { id: searched_talent_id })
The problem is that when displaying each creative it only returns the matching talent.
So rendering:
<% #creatives.each do |creative| %>
<% creative.talents.each do |talent| %>
<%= talent.name %>
<% end %>
<% end %>
Only shows the talent matched by the query, not all of them. I.e. the creative has multiple talents.
If I change the code to include a call to .all.
<% #creatives.each do |creative| %>
<% creative.talents.all.each do |talent| %>
<%= talent.name %>
<% end %>
<% end %>
Then I do get all talents BUT the database is hit with query for each creative.
Can I avoid this? I.e. eager loading all talents in creative and not getting limited by the one i search on!?
You can solve this by using a subquery:
#creatives = Creative.includes(:talents, user: [:profile_image_attachment])
.where(
id: Creative.joins(:talents)
.where(talents: { id: searched_talent_id })
)
This creates the following SQL:
SELECT
"creatives".* FROM "creatives"
WHERE
"creatives"."id" IN (
SELECT "creatives"."id"
FROM "creatives"
INNER JOIN "creatives_talents" ON "creatives_talents"."creative_id" = "creatives"."id"
INNER JOIN "talents" ON "talents"."id" = "creatives_talents"."talent_id"
WHERE "talents"."id" = $1
)
LIMIT $2
This only applies the WHERE clause to the subquery instead of the rows fetched by .includes.
I have a collection of products users have purchased, grouped by their name, so I can count the number of unique products and how many of each has been purchased:
Controller:
#line_items = Spree::LineItem.joins(:order).where(spree_orders: {state: "complete"})
#products = #line_items.group_by(&:name)
View:
<% #products.each do |name, line_items| %>
<%= name %> - <%= line_items.count %><br>
<% end %>
Is there a way to order the .each loop so that it descends by line_items.count?
Thanks
It will perform better getting the correct data directly from the db:
#products = #line_items.group(:name).order("count_all DESC").count
That will give you the names and counts directly, e.g.
# => { "line_1" => 3, "line_2" => 2, "line_3" => 8 }
There's a bit of Rails magic at work here: the SQL generated using group, order and count will look like:
SELECT COUNT(*) AS count_all, name AS name FROM spree_line_items GROUP BY name ORDER BY count_all DESC
That's where count_all comes from: Rails attaches it to the count column automatically.
Then you can plug this directly into your view:
<% #products.each do |name, line_item_count| %>
<%= name %> - <%= line_item_count %><br>
<% end %>
Alternatively, if you're using the instance variable elsewhere, here's a simple Ruby solution:
#products = #line_items.group_by(&:name).sort_by { |_k, line_items| line_items.count }.reverse
This simply uses sort_by to get records ordered by the relevant count, then reverses to get decending order. There's a good benchmark on doing this here.
Hope that helps - let me know how you get on / if you've any questions.
All right, I've been stuck on this problem for 6 days. Good news: I've learned an immeasurable amount about the MVC structure while trying to self-solve. Bad news: still haven't solved it.
cue help
In my app, I have 3 data sources (technically 4 if you count sessions). A user who has many budgets and has many transactions.
Controller
I am currently summarizing the combination of the 3 in the User controller:
#users_controller.rb
def my_budget
#transaction = current_user.transactions
#month = params[:search]
#budget = current_user.budgets
#user = current_user
end
View
In my view, I display each user's budget (Column 1) in comparison to the sum of their transactions (Column 2) based on type (for this example, 11 correlates to "total income" in my database). Then, I calculate the difference between the two (Column 3).
#my_budget.html.erb
<% #user.budgets.each do |budget| %>
<tr>
<td><h3 class="section-header">Current Income:</h3></td>
</tr>
<tr>
<td class="section-sub-header">Gross:</td>
<td class="section-value"><%= number_to_currency(budget.income_per_month) %></td>
<td class="section-value"><%= number_to_currency(#user.get_transaction(current_user, 11)) %></td>
<td class="section-value difference-value"><%= number_to_currency(#user.total_difference(budget.income_per_month, current_user, 11)) %></td>
</tr>
Obviously, this information is useless. For example, if the user keeps track of their transactions all through April, they will forever have a negative budget because Column 2 and Column 3 currently compare the monthly budget to ALL transactions occurring since the user created an account. So, I have tried many attempts at creating an ability to select a month to view, then summarize the data by that value.
Partial Search
In one of my partials, located at the top of the view I have (specially formatted to where the user can only select a month and day in the format MM/YYYY):
#_classic_view.html.erb
<%= form_tag(my_budget_path, :method => "get") do %>
<%= text_field_tag :search, params[:search], name: "startDate", id: "startDate", class: "date-picker form-control", placeholder: "Select Month" %>
<%= submit_tag "Sort", :name => nil, class: "btn-submit-user text-center", style: "padding: 5px" %>
<% end %>
Model
Here is an excerpt of my model:
#user.rb
class User < ApplicationRecord
attr_accessor :search
has_many :budgets
has_many :transactions
...
# attempt at pulling the param via search
def self.search(search)
where("created_at LIKE ?", "%#{#search}%")
end
...
# Get transaction sum for the specific sub-category
def get_transaction(user, type)
if search
total_value = user.transactions.where("transactions.sub_category" => "#{type}").search(#search).sum(:amount)
else
total_value = user.transactions.where("transactions.sub_category" => "#{type}").sum(:amount)
end
end
My Attempt at console.log Ruby Style
I have been through the run-around of accessing the params just to see what I'm working with in the view (oh how I wish Ruby had a simple console.log):
#my_budget.html.erb
<h2 class="text-center"><%= #user.current_month(#month) %> Summary</h2>
#user.rb
def current_month(val)
if val
current = val
else
current = Date.today.strftime("%B %Y")
end
current
end
Please help with:
SOLVED Is my use of get_transaction() in the User.rb correct? Specifically, how I call Transaction. as an entire class.
I know you should not access params according to the MVC structure, But how do I pass this information to my model or view (since I'm not traditionally looping through a #transaction variable)?
Is my search/sort method even viable for my implementation in the model, view, and controller?
Thank you!!
Edit:
Solved #1: I was not using Transaction correctly. I did this because you cannot access the "current_user" method in the model, but you CAN pass it in as an argument in the view. I adjusted my code to display this fix.
create a scope in model like
class Transaction < ApplicationRecord
scope :by_month, -> (year, month_name = '') {
month = Date::MONTHNAMES.index(month_name.capitalize)
first_day_of_month = Date.new(year, month, 1)
last_day_of_month = Date.new(year, month, -1) # Gives you the last day of month
where("transactions.created_at between ? and ?", first_day_of_month, last_day_of_month)
}
end
This will act as a filter. Access it from controller wisely.
If you want to find sum of :amount then you can do like
Transaction.by_month(2016, 'October').sum(:amount)
For transaction sum of a user for that month
User.find(2).transactions.by_month(2016, 'October').sum(:amount)
I have two tables which are one to many (1 challenge to many entry)
I want to get the last entry for all challenges but I also want to get the title of the challenge for that last entry.
So far I have:
def index
#discovers = Challenge.all.map{|c| c.entries.last}
end
How to I also add the fact I want the Challenge.title?
def index
#challenges = Challenge.all
end
Then inside your view
<% #challenges.each do |challenge| %>
<%= challenge.title %> # will give you challenge title
<%= challenge.entries.last %> # will give you last entry for the challnge
<% end %>
I have multiple arrays of instances of ActiveRecord subclass Item that I need to loop through an print in accordance to earliest event. In this case, I need to print print out payment and maintenance dates as follows:
Item A maintenance required in 5 days
Item B payment required in 6 days
Item A payment required in 7 days
Item B maintenance required in 8 days
I currently have two queries for finding maintenance and payment items (non-exclusive query) and something like the following to output them:
<%- item_p = nil -%>
<%- item_m = nil -%>
<%- loop do -%>
<% item_p ||= #items_p.shift %>
<% item_m ||= #items_m.shift %>
<%- if item_p.nil? and item_m.nil? then break -%>
<%- elsif item_p and (item_m.nil? or item_p.paymt < item_m.maint) then -%>
<%= item_p.name %> payment required in ...
<%- elsif item_m and (item_p.nil? or item_m.maint < item_p.paymt) then -%>
<%= item_m.name %> maintenance required in ...
<%- end -%>
<%- end -%>
Any way to cleanup the readability of the above (ugly) code?
Embrace duck-typing and make sure that your objects are polymorphic. You want your payment items to be comparable with maintenance items, in order to sort them.
So, suppose you have a Payment and a Maintenance class:
module Due
include Comparable
# Compare this object with another. Used for sorting.
def <=>(other)
self.due <=> other.due
end
end
class Payment < ActiveRecord::Base
include Due
alias_method :due, :payment
def action
"#{name} requires payment"
end
end
class Maintenance < ActiveRecord::Base
include Due
alias_method :due, :maintenance
def action
"#{name} requires maintenance"
end
end
See how we create an action, due and <=> method in both classes? We also took care to include the Ruby built-in module Comparable. This allows us to do the following:
# Assuming 'payment' and 'maintenance' are date fields...
a = Payment.new :payment => 3.days.from_now
b = Maintenance.new :maintenance => 2.days.from_now
[a, b].sort
#=> [b, a]
The view then becomes as simple as:
<% (#payment_items + #maintenance_items).sort.each do |item| %>
<%= item.action %> in <%= distance_of_time_in_words_to_now(item.due) %><br/>
<% end %>
I'm sure I haven't got the details of your implementation right, but I hope this gives you an idea of how to approach your problem.
This is quick and dirty (i.e. not optimized):
# In your controller:
#items = #items_p.map{ |item| {:item => item, :days => item.paymt, :description => "payment"} }
#items += #items_m.map{ |item| {:item => item, :days => item.maint, :description => "maintenance"} }
#items = #items.sort_by{ |item| item[:day] }
# In your view:
<% #items.each do |item| %>
<%= item[:item].name %> <%= item[:description] %> required in <%= item[:days] %> days
<% end %>
You're doing way too much in your view. Really you should figure out all of this in the controller and pass through a cleaned up structure that you can iterate over for display purposes.
As an example:
length = [ #items_p.length, #items_m.length ].sort.last
#messages = [ ]
length.times do |i|
item_p = #items_p[i]
item_m = #items_m[i]
if (item_p and (item_m and item_p.paymt < item_m.maint) or !item_m)
#messages << "#{item_p.name} payment required in ..."
elsif (item_m and (item_p and item_m.maint < item_p.paymt) or !item_p)
#messages << "#{item_m.name} maintenance required in ..."
end
end
You would then iterate over #messages as required.
The real problem here is that you haven't structured these objects for this sort of thing strategically speaking. It would be nice if you had a single method for the due date instead of having to differentiate between paymt and maint according to the type. Likewise, it would be better if both of these were paired up into an Array instead of supplied separately.
If you had them in [ p, m ] pairs you could iterate much more simply:
items.each do |pair|
first_due = pair.compact.sort_by(&:due).first
#messages << "#{first_due.name} #{first_due.action} required in ..."
end
The action method would return payment or maintenance as required.