I have a site where customers can purchase tickets for an event and created a Reporter::EventTickets class to generate a hash for the relevant information I need. Reporter::EventTickets is called in a Reports controller, and ultimately I want to view this on an admin report page.
I cannot get the information to display on my table in my haml view file!
I've tried everything I can think and am at a complete loss for what to do.
Information is being saved to the database (PostgreSQL) successfully and the EventTickets class is transforming data properly (passing RSpec tests).
Code:
class Reporter::EventTickets
include Virtus.model
def events
products.map do |product|
line_items = LineItem.includes(:order)
.where("orders.completed_at IS NOT NULL")
.where(purchasable_type: "Product", purchasable_id: product.id)
.references(:order)
ticket_purchases = line_items.map do |line_item|
order = line_item.order
[order.bill_address_full_name, order.email, line_item.quantity]
end
total = ticket_purchases.reduce(0) { |sum, purchase| sum + purchase.last }
{
date: product.event_date,
name: product.name,
total: total,
purchases: ticket_purchases.sort
}
end
end
private
def products
Ticket.where("event_date >= ?", week_ago_in_time_zone).order("event_date ASC")
end
def week_ago_in_time_zone
(Time.current - 7.days).to_date
end
end
Controller:
def event_tickets
#reporter = Reporter::EventTickets.new
#csv_link = admin_reports_event_tickets_path(format: :csv)
respond_with #reporter
end
Models:
class Order < ActiveRecord::Base
has_many :line_items, dependent: :destroy
class LineItem < ActiveRecord::Base
belongs_to :order, touch: true
belongs_to :purchasable, polymorphic: true
class Product < ActiveRecord::Base
include Purchasable
has_many :line_items, as: :purchasable
View:
= render partial: "admin/shared/header", locals: {title: "Event Ticket Purchases"}
.container-fluid.padded
= render partial: "admin/shared/notifications"
.row-fluid.hidden-print
= link_to "Download CSV", #csv_link, class: "btn btn-blue"
.row-fluid
- #reporter.events.each do |event|
%h3
= event[:date].strftime("%_m/%e")
= event[:name]
.box
%table.table.table-normal
%thead
%tr
%td Name
%td Email
%td Tickets
%tbody
- event[:purchases].each do |purchase|
%tr
- purchase.each do |column|
%td= column
%tr
%td{:colspan => "2"}
%b TOTAL
%td
%b= event[:total]
There are no errors from Rails and the page loads with the title and button. The table just does not populate.
As debugged in comments, your code is fine. With live data, this:
Ticket.where("event_date >= ?", week_ago_in_time_zone).order("event_date ASC")
...however, is returning an empty result set. So this:
products.map do |product|
...
end
...is returning an empty array.
Your RSpec test passed because you had Ticket records in your test data that met the criteria and, therefore, products.map was returning a non-empty array.
Related
I want to return a list of parks that have multiple tags applied. For example, users should be able to find a park that has all 3 tags:
Camping
Shade
BBQ
I've implemented a form where the user can select multiple tags using Ransack _in predicate, but it returns parks with ANY of the tags, rather than ALL of the tags. The _in_all predicate doesn't return any results when more than one tag is selected.
I found what looks like a promising approach in two different threads, but haven't succeeded in getting it to work in my code:
Searching a string array in an association fails with Ransack - Their solution:
ransacker :roles do
Arel.sql("array_to_string(roles, ',')")
end
Search an array of values with Ransack - Their solution:
ransacker :rep_code_list do
Arel.sql("array_to_string(rep_code_list, ',')")
end
In the console I can return the array I want with parks.tags.ids. So I thought I could somehow convert that array into a string and use Ransack's cont predicate to search. Here's my attempted solution but getting this error: ActionView::Template::Error (PG::UndefinedColumn: ERROR: column "tags_ids" does not exist LINE 1: ... = INNER JOIN "tagifications" ON "tagifications"."park_id" = "parks"."id" INNER JOIN "tags" ON "tags"."id" = "tagifications"."tag_id" WHERE array_to_string(tags_ids, ',') ILIKE '%["21"]%'
What am I doing wrong?
models/park.rb
class Park < ApplicationRecord
has_many :tagifications, dependent: :destroy
has_many :tags, through: :tagifications
ransacker :tags_ids do
Arel.sql("array_to_string(tags_ids, ',')")
end
end
controllers/parks_controller.rb
class ParksController < ApplicationController
def index
#parks = #q.result(distinct: true).joins(:visited_users, :favorited_users, :tags).near(#user_coordinates, 100000000).paginate(page:params[:page], :per_page => 24)
#tags = Tag.all
end
end
views/parks/index.html.erb
<% #tags&.each do |tag| %>
<%= f.check_box :tags_ids_cont, { multiple: true, include_hidden: false, class: "btn-check", onchange: 'this.form.submit();', id: tag.name.titleize+"Checkbox" }, tag.id %>
<%= f.label :tags_ids, tag.name.titleize, class: 'btn btn-outline-secondary border mb-1', for: tag.name.titleize+"Checkbox" %>
<% end %>
I have a model that returns the count of records in an un-associated model that match a given value:
def number_of_orders
Order.where(delivery_date: date).size
end
My model has a date attribute and I want to find all the orders with a matching date.
My controller looks like this:
def index
#schedule_dates = ScheduleDate.where(date: 1.week.ago..Float::INFINITY)
end
My problem is that when I call number_of_orders in the view, I end up with N+1 queries:
<% #schedule_dates.each do |date| %>
<tr>
<td><%= date.number_of_orders %></td>
I've tried includes in the controller:
def index
#schedule_dates = ScheduleDate.where(date: 1.week.ago..Float::INFINITY).includes(:orders)
end
But Rails complains that the association is not found.
Is it possible to avoid N+1 queries without creating an association with the two models?
You can add has_many association to ScheduleDate
has_many :orders, foreign_key: 'delivery_date', primary_key: 'date'
then you can use includes in the search query
#schedule_dates = ScheduleDate.includes(:orders).where(date: 1.week.ago..Float::INFINITY)
and in the view
<% #schedule_dates.each do |date| %>
<tr>
<td><%= date.orders.length %></td>
i have a country model and a travel note model. A country has many travel notes and a travel note belongs to one country.
class TravelNote < ActiveRecord::Base
default_scope { order(created_at: :desc) }
belongs_to :country
has_many :chapters
before_destroy { draft? }
validate :validates_uniqueness_of_draft, on: :create
enum status: { draft: 0, published: 1, archived: 2 }
enum advice_against: { no: 0, general: 1, tourists: 2 }
scope :country, ->(country_id) { where(country_id: country_id) }
# further methods omitted...
end
class Country < ActiveRecord::Base
default_scope { order(:iso_code) }
has_many :travel_notes
end
in app/controllers/countries_controller.rb:
class CountriesController < ApplicationController
def index
#countries = Country.includes(:travel_notes)
end
# rest of the class omitted...
end
in app/views/countries/index.html.haml:
#countries.each do |country|
%tr
%td= link_to country.name_de, country_travel_notes_path(country)
%td= TravelNote.published.country(country.id).first.try :published_at
because of performance reason i want to remove TravelNote.published.country(country.id).first.try :published_at so that there is not hundreds of database queries anymore instead just an array of an equivalent sql query:
select * from countries
left join travel_notes
on countries.id=travel_notes.country_id
how can i achieve it?
Apparently you are trying to eager-load the "travel_notes" associated to the country:
Country.includes(:travel_notes).where(travel_notes: { status: 1} )
so your code will be:
class CountriesController < ApplicationController
def index
#countries = Country.includes(:travel_notes).where(travel_notes: { status: 1} )
end
# rest of the class omitted...
end
#countries.each do |country|
%tr
%td= link_to country.name_de, country_travel_notes_path(country)
%td= country.travel_notes.first.published_at
You could write a custom scope to include only the published notes.
something like
scope :include_published, -> { proc do |_|
joins("LEFT OUTER JOIN (
SELECT b.*
FROM travel_notes b
WHERE published = 1
GROUP BY b.country_id
) notes_select ON notes_select.country_id = countries.id"
).select("countries.*").select("#{insert what attributes you want to include }")
end.call(self, counted_model) }
You have to include the attributes you want from the note in the second select clause then they will be included in the country active record result as methods with the same name.
The SQL-query can be written prettier, I just threw something together...
I use a similar technique in my project but in order to include counts of associated objects.
My controller is retrieving the follow:
def index
#segments = Segment.all
#products = Product.all
end
But it is not what I want. I want to iterate products based on its segment_id. Something like this:
def index
#segments = Segment.all
#products = Product.where(segment_id: x)
end
The problem is: how to pass x from view to controller?
My view is:
- #segments.each do |s|
- #products.each do |p|
This is a product from #{s.name}.
Ok, where's the issue? I'm not showing specific products of specific segments. I'm showing products of all segments. What I need is something like this:
- #segments.each do |s|
- #products(segment_id: s.id).each do |p|
This is a product from #{s.name}.
Can you understand?
Set up has_many association between Segment and Product model.
class Segment < ActiveRecord::Base
has_many :products
end
class Product < ActiveRecord::Base
belongs_to :segment
end
Add segment_id in the products table.
Generate the migration with
rails g migration add_segment_id_to_products segment_id:integer:index
Run rake db:migrate
Setting up the association would give you dynamic method products for an instance of segment which you can use in the view for iteration.
Then update the index action as:
def index
#segments = Segment.all
end
Update the view as:
- #segments.each do |s|
- s.products.each do |p|
This is a product from #{s.name}.
Would this do?
def index
#segments = Segment.includes(:products).all
end
And in your view:
- #segments.each do |s|
- s.products.each do |p|
This is product #{p.name} from segment #{s.name}
You can add where conditions to the query for both segmens and products if you want to filter them in any way you need like this.
Segment.includes(:products).where(:seg_val => 'something', :product => [:prod_val => 'other']).all
I have a Product model:
class Product < ActiveRecord::Base
belongs_to :subcategory
define_index do
# fields
indexes subcategory.name, :as => :subcategory, :sortable => true, :facet => true
# attributes
has subcategory_id, created_at, updated_at
#properties
set_property :delta => true
Now, suppose that a user updates a subcategory name, which is the proper way to update the products delta index?
According to this documentation: http://freelancing-god.github.com/ts/en/deltas.html, a save message should be sent to the product, so in this case I should go for each product related with the subcategory and send the save message, something like this:
class Subcategory < ActiveRecord::Base
has_many :products
after_save :set_product_delta_flag
private
def set_product_delta_flag
products.each { |product|
product.delta = true
product.save
}
end
end
I think that this is overkilling because we have like 100.000 products per subcategory.
Is this the correct way to update the delta index? Am I missing something?
After adding this:
def set_product_delta_flag
Product.update_all ['delta = ?', true], ['subcategory_id = ?', id]
Product.index_delta
end
I'm always receiving this error:
NoMethodError (undefined method `index_delta' for #):
So, the solution to this problem was to send the message *define_indexes* to the Product model.
After fixing this issue, everything was ok, but the delta_index was not correctly updated, I needed to do save twice to the subcategory model.
So my final solution is this one:
after_commit :set_product_delta_flag
private
def set_product_delta_flag
Product.define_indexes
Product.update_all ['delta = ?', true], ['subcategory_id = ?', id]
Product.index_delta
end
Using after_commit and define_indexes is the correct solution? Its the only one that I've found.
Try the following instead:
def set_product_delta_flag
Product.update_all ['delta = ?', true], ['subcategory_id = ?', id]
Product.index_delta
end
A single SQL statement, a single delta re-indexing. Should perform far better :)