I have the following models in my Ruby on Rails application in which I'm using Postgres Database:
class Sale < ApplicationRecord
has_many :line_items
end
class LineItem < ApplicationRecord
belongs_to :sale
end
There are now 2 things I want to achieve:
First, I'd like to create an index page in ActiveAdmin for Sales, so I could, for each sale, display the sum of line items prices.
What I already tried, which doesn't work very well(it's very slow):
ActiveAdmin.register Sale do
actions :index
remove_filter :line_items
index do
column :created_at
column :price do |sale|
sale.line_items.sum(:price)
end
end
end
Second, I'd like to make this column sortable - would it be possible?
My suggestion for your case above you can just group based sale_id and then sum the price and to the sort with method below, reverse will get you descending order
LineItem.group(:sale_id).sum(:price).sort_by{|k, v| v}.reverse
if you need probably top ten you can use first(10)
LineItem.group(:sale_id).sum(:price).sort_by{|k, v| v}.reverse.first(10)
Try registering a database view that sums the prices. The documentation on custom sorting should still apply. Links to tips and tricks like this can be found on the wiki.
Related
I have three Models. Both makes and models table have name column inside.
class Review < ActiveRecord::Base
belongs_to :model
end
class Model < ActiveRecord::Base
belongs_to :make
has_many :reviews
end
class Make < ActiveRecord::Base
has_many :models
end
In Active Admin on Index Page I want to sort reviews by make and model.
I figured out how to sort it by model, because it has key the reviews table.
Tried a lot of things. Nothing works to sort by make name. I was able to list though.
ActiveAdmin.register Review do
column "Model", sortable: 'model_id' do |p|
p.model.name
end
column "Make", sortable: 'make_id' do |review|
review.model.make.name
end
end
Only sorting by model name works. I think if I add make_id to Reviews it will be working, but it seems redundant, cause a chain like review.model.make perfectly works
Let's say a review is a score between 1 and 5.
You will have to compute the average value for each make.
To compute an average value for a make, do:
reviews_sum = 0
total_reviews = 0
make.models.each do |model|
model.reviews.each do |review|
reviews_sum += review
total_reviews += 1
end
end
Then:
average_review = reviews_sum / total_reviews.to_f # I am using to_f to convert total_reviews from integer to float. This way you can have a float result.
I advice you to create an make_average_review(make_id, average_review)
This way you can store/update your average review for every make and then sort by make
I have 3 models and tables Products, Customers, Buyers and there are has_and_belongs_to_many relationship among them. And I have another model and table sells. I need to get value from all of the above 3 tables in sells/new page. Do I have to use any association among them? How can I get the values?
I want product_id, product_name, customer_id, customer_name in views/sells/new.html.erb file I don't understand how can I get that
First of all it should be sales table and the Sale model. But anyway, from the view (or helper), you can do:
Product.all # gives you all products
# or fetch just the columns you want:
Product.select( [:id, :name] )
Same goes for customers (i.e. Customer.all etc.).
It's not an orthodox way to do it but it will work. With Erb you'll need <% ... %> or <%= ... %> of course.
Add user_id and product_id to sells table
Class User
has_many :sells
end
Class Product
has_many :sells
end
Class Sells
belongs_to :user
belongs_to :user
end
Then do on sell show page
sell.user_id
sell.user.name
sell.product_id
sell.product.name
Hope this is what you need or least give an idea :)
Rails 3.1
I'll simplify my application to get at my question.
I have two tables: Items and Reviews
Items has a column "Average_Rating" and Reviews has a column "Item_ID" and "Rating"
For each item, I'd like to store the average rating for its corresponding reviews. Although I can't figure it out, I feel like what I want to do is add something to the create and update methods in the Reviews Controller along the lines of:
#review = Review.find(params[:id])
#item = Item.find(#review.Item_ID)
reviews_to_sum = Reviews.find_by_item_id(#item.id)
#item.Average_Rating = reviews_to_sum.Rating.sum/reviews_to_sum.count
I recognize, however, that the above probably isn't close to correct... I'm a beginner and I'm stuck.
And I do want to store the Average_Rating in the database, as opposed to calculating it when I need it, for a variety of reasons.
class Item < ActiveRecord::Base
has_many :reviews
end
class Review < ActiveRecord::Base
belongs_to :item
after_save do
item.update_attributes average_rating: item.reviews.average(:rating)
end
end
I'm learning my way around Rails and am working on a sample app to keep track of beer recipes.
I have a model called Recipe which holds the recipe name and efficiency.
I have a model called Ingredient which is using STI - this is subclassed into Malt, Hop, and Yeast.
Finally, to link the recipes and ingredients, I am using a join table called rec_items which holds the recipe_id, ingredient_id, and info particular to that recipe/ingredient combo, such as amount and boil time.
Everything seems to be working well - I can find all my malts by using Malt.all, and all ingredients by using Ingredient.all. I can find a recipe's ingredients using #recipe.ingredients, etc...
However, I'm working on my recipe's show view now, and am confused as to the best way to accomplish the below:
I want to display the recipe name and associated info, and then list the ingredients, but separated by ingredient type. So, if I have a Black IPA # 85% efficiency and it has 5 malts and 3 hops varieties, the output would be similar to:
BLACK IPA (85%)
Ingredient List
MALTS:
malt 1
malt 2
...
HOPS:
hop 1
...
Now, I can pull #recipe.rec_items and iterate through them, testing each rec_item.ingredient for type == "Malt", then do the same for the hops, but that doesn't seem very Rails-y nor efficient. So what is the best way to do this? I can use #recipe.ingredients.all to pull all the ingredients, but can't use #recipe.malts.all or #recipe.hops.all to pull just those types.
Is there a different syntax I should be using? Should I using #recipe.ingredient.find_by_type("Malt")? Doing this in the controller and passing the collection to the view, or doing it right in the view? Do I need to specify the has_many relationship in my Hop and Malt models as well?
I can get it working the way I want using conditional statements or find_by_type, but my emphasis is on doing this "the Rails way" with as little DB overhead as possible.
Thanks for the help!
Current bare-bones code:
Recipe.rb
class Recipe < ActiveRecord::Base
has_many :rec_items
has_many :ingredients, :through => :rec_items
end
Ingredient.rb
class Ingredient < ActiveRecord::Base
has_many :rec_items
has_many :recipes, :through => :rec_items
end
Malt.rb
class Malt < Ingredient
end
Hop.rb
class Hop < Ingredient
end
RecItem.rb
class RecItem < ActiveRecord::Base
belongs_to :recipe
belongs_to :ingredient
end
recipes_controller.rb
class RecipesController < ApplicationController
def show
#recipe = Recipe.find(params[:id])
end
def index
#recipes = Recipe.all
end
end
Updated to add
I'm now unable to access the join table attributes, so I posted a new question:
Rails - using group_by and has_many :through and trying to access join table attributes
If anyone can help with that, I'd appreciate it!!
It's been a while since I've used STI, having been burned a time or two. So I may be skipping over some STI-fu that would make this easier. That said...
There are many ways of doing this. First, you could make a scope for each of malt, hops, and yeast.
class Ingredient < ActiveRecord::Base
has_many :rec_items
has_many :recipes, :through => :rec_items
named_scope :malt, :conditions => {:type => 'Malt'}
named_scope :hops, :conditions => {:type => 'Hops'}
...
end
This will allow you to do something line:
malts = #recipe.ingredients.malt
hops = #recipe.ingedients.hops
While this is convenient, it isn't the most efficient thing to do, database-wise. We'd have to do three queries to get all three types.
So if we're not talking a ton of ingredients per recipe, it'll probably be better to just pull in all #recipe.ingredients, then group them with something like:
ingredients = #recipe.ingredients.group_by(&:type)
This will perform one query and then group them into a hash in ruby memory. The hash will be keyed off of type and look something like:
{"Malt" => [first_malt, second_malt],
"Hops" => [first_hops],
"Yeast" => [etc]
}
You can then refer to that collection to display the items however you wish.
ingredients["Malt"].each {|malt| malt.foo }
You can use group_by here.
recipe.ingredients.group_by {|i| i.type}.each do |type, ingredients|
puts type
ingredients.each do |ingredient|
puts ingredient.inspect
end
end
The utility of STI in this instance is dubious. You might be better off with a straight-forward categorization:
class Ingredient < ActiveRecord::Base
belongs_to :ingredient_type
has_many :rec_items
has_many :recipes, :through => :rec_items
end
The IngredientType defines your various types and ends up being a numerical constant from that point forward.
When you're trying to display a list this becomes easier. I usually prefer to pull out the intermediate records directly, then join out as required:
RecItem.sort('recipe_id, ingredient_type_id').includes(:recipe, :ingredient).all
Something like that gives you the flexibility to sort and group as required. You can adjust the sort conditions to get the right ordering. This might also work with STI if you sort on the type column.
I'm using meta_search to sort columns in a table. One of my table columns is a count of the associated records for a particular model.
Basically it's this:
class Shop < ActiveRecord::Base
has_many :inventory_records
def current_inventory_count
inventory_records.where(:current => true).count
end
end
class InventoryRecord < ActiveRecord::Base
belongs_to :shop
#has a "current" boolean on this which I want to filter by as well
end
In my Shop#index view I have a table that lists out the current_inventory_count for each Shop. Is there anyway to use meta_search to order the shops by this count?
I can't use my current_inventory_count method as meta_search can only use custom methods that return an ActiveRecord::Relation type.
The only way I can think about doing this is to do some custom SQL which includes the count in a "virtual" column and do the sorting by this column. I'm not sure if that's even possible.
Any Ideas?
I'm using Rails 3.0.3 and the latest meta_search.
To add extra columns to a result set...
In Shop.rb ..
scope :add_count_col, joins(:inventory_records).where(:current=>true).select("shops.*, count(DISTINCT inventory_records.id) as numirecs").group('shops.id')
scope :sort_by_numirecs_asc, order("numirecs ASC")
scope :sort_by_numirecs_desc, order("numirecs DESC")
In shops_controller.rb index method
#search = Shop.add_count_col.search(params[:search])
#etc.
In index.html.erb
<%= sort_link #search, :numirecs, "Inventory Records" %>
Found the sort_by__asc reference here: http://metautonomo.us/2010/11/21/metasearch-metawhere-and-rails-3-0-3/
Rails has a built-in solution for this called counter_cache
Create a table column named "inventory_records_count" on your shops table.
class Shop < ActiveRecord::Base
has_many :inventory_records, :counter_cache => true
end
http://asciicasts.com/episodes/23-counter-cache-column