I am creating a Rails 3.2 web app.
In this app I got four tables. Project, Task, Article and Item.
What I want to do is to get all the task values (prices from article) summed up
in a single call.
This is what I tried and it works, but is it the best way of doing it?
#project.tasks.where("status = ?", "completed").joins(:articles).sum(:price)
Task table
class Task < ActiveRecord::Base
has_many :articles
has_many :items, :through => :articles
end
Article Join Table
class Article < ActiveRecord::Base
belongs_to :task
belongs_to :item
attr_accessible :account_id, :amount, :price, :item_id, :task_id
end
Item table
class Item < ActiveRecord::Base
has_many :articles
has_many :tasks, :through => :articles
end
to sum it up it looks ok the way you did it, but also you can prettify your code:
project.rb
has_many :completed_tasks, class: 'Task', :conditions => {:status => 'completed'}
controller
#project.completed_tasks.joins(:articles).sum(:price)
Related
I have the following models:
class Productmainclass < ActiveRecord::Base
attr_accessible :name, :id, :maintext
has_many :producttaggings, :dependent => :destroy
has_many :products, :through => :producttaggings
has_many :productsubclasses
end
class Productsubclass < ActiveRecord::Base
attr_accessible :name, :id, :maintext
has_many :producttaggings, :dependent => :destroy
has_many :products, :through => :producttaggings
belongs_to :productmainclass
end
class Product < ActiveRecord::Base
attr_accessible :name, :productimage, :size, :description, :price
has_many :producttaggings, :dependent => :destroy
has_many :productsubclasses, :through => :meteoritetaggings
has_many :productmainclasses, :through => :meteoritetaggings
mount_uploader :productimage, ProductimageUploader
end
class Producttagging < ActiveRecord::Base
belongs_to :product
belongs_to :productsubclass
belongs_to :productmainclass
attr_accessible :product_id, :productsubclass_id, :productmainclass_id
end
I now want to create a Product with FactoryGirl and Capybara. In the spec I simply have:
product = FactoryGirl.create(:product)
In my factories.rb I have:
factory :product do
name "Blue glass"
description "Description text of product"
productimage File.new(File.join(::Rails.root.to_s, "spec/factories/", "testimage.jpg"), 'rb')
productsubclass
productmainclass
end
factory :productsubclass do
name "Colored glasses"
productmainclass
end
factory :productmainclass do
name "Glasses"
end
Running the test I get:
Failure/Error: product = FactoryGirl.create(:product)
NoMethodError:
undefined method `productsubclass=' for #<Product:0xcd42090>
I think the way you have it setup would work if you were dealing with a situation where :product belonged to productsubclass, then the product and the productsubclass would be created with the product.productsubclass_id nicely inserted and all would be fine, but that's clearly not your structure, so we'd have to use another way. I think the link that #depa noted is the right way to go, specifically the 'Basic has many associations' section in this document: http://robots.thoughtbot.com/aint-no-calla-back-girl although you have the added complexity of a has_many through. But essentially your looking at a situation where you create an object and then after that you trigger another create to make the many's. Hope this makes sense :)
** Update **
Here's another approach which might be a little limited but you could just create the records from the other direction. So, if you just want one record in each object/table how about this:
FactoryGirl.define do
factory :producttagging do
product
productsubclass
productmainclass
end
end
The image shows part of my data model. I would like to fetch all items that are associated with a user (through organizations and items_group). How should I change the models and write this query in the controller? Using :through => organizations I can get all items_groups but I don't how to include one more relation to query related items.
class User < ActiveRecord::Base
has_and_belongs_to_many :organizations
has_many :items_groups, :through => :organizations
end
class Organization < ActiveRecord::Base
has_and_belongs_to_many :users
has_and_belongs_to_many :items_groups
has_many :items, :through => :items_groups
end
class ItemsGroup < ActiveRecord::Base
has_many :items, :inverse_of => :items_group
has_and_belongs_to_many :organizations
has_many :users, :through => :organizations
end
I think you might have to do it back-to-front and find items joined back to your user.
You could define method like this in your User class:
def items
Item.joins(:items_group => {:organizations => :users}).where("users.id" => self.id).select("distinct items.*")
end
The items it returns will be read-only because of the explicit select but I think you'll want that to avoid returning individual items more than once.
If you set in your models the relationships this should work:
users.organizations.item_groups.items
Though for it to work your models should contain this:
class User < ActiveRecord::Base
has_many :organizations, :through => :organization_users
end
class Organization < ActiveRecord::Base
has_many :item_groups, :through => :items_groups_organizations
end
class ItemsGroup < ActiveRecord::Base
belongs_to :item
end
Hope it works for you!
I have the following models and running rails 3.01:
# file: app/models/product.rb
class Product < ActiveRecord::Base
has_many :categories, :through => :product_categories
has_many :product_categories, :dependent => :destroy
accept_nested_attributes_for :product_categories
end
# file: app/models/category.rb
class Category < ActiveRecord::Base
has_many :products, :through => :product_categories
has_many :product_categories, :dependent => :destroy
end
# file: app/models/product_category.rb
class ProductCategory < ActiveRecord::Base
belongs_to :product
belongs_to :category
end
ProductCategory is my join table. What do I call in my products form? Do I build on the Categories table or the ProdcutCategories table? Im just really confused on how/which models Im supposed to nest in my products from. Thanks!
The model is already configured to accept attributes for the products_categories association. In your form just reference it like this:
<%= f.fields_for :products_categories do |pc| %>
# fields go here
Remember that you will need to build new objects for the products_categories association before this form will render anything:
products.products_categories.build
I basically followed the ROR guide, http://guides.rubyonrails.org/association_basics.html#the-has_many-through-association, to create the relationship models as shown below.
Because of the through association, I figured that #user.trips would give you both the trips that the user created and the trips that belong to the user. However, when I do #user.trips.count in console, the result was only the number of trips that the users created; the trips that belonged to the user through the 'group' association was not counted.
Question: How do I get my view to display both the trips that the user created and the trips that the user belongs to through 'group'?
user/show.html.erb
<% unless #user.all_trips.empty? %>
<% #user.all_trips.each do |trip| %>
<!-- Content -->
<% end %>
<% end %>
user.rb
class User < ActiveRecord::Base
has_many :group_trips, :through => :groups,
:source => :trip
has_many :trips, :dependent => :destroy
has_many :groups
def all_trips
self.trips | self.group_trips
end
end
trip.rb
class Trip < ActiveRecord::Base
belongs_to :user
belongs_to :traveldeal
has_many :groups
has_many :users, :through => :groups
end
group.rb
class Group < ActiveRecord::Base
belongs_to :trip
belongs_to :user
end
Thanks!
Edit: Modified code per TSherif's partial solution.
Edit 2: Fixed up the all_trips method. Everything appears to work for me at this point.
Oh! I think I get what you're trying to do and why it's a problem. I was wondering why has_many :trips was called twice. But from what I understand, you have two different User-Trip relationships. These two can't have the same name, otherwise one will hide the other. Try something like this:
class User < ActiveRecord::Base
has_many :group_trips, :through => :groups,
:class_name => "Trip"
has_many :trips, :dependent => :destroy
has_many :groups
def all_trips
Trip.joins(:groups).where({:user_id => self.id} | {:groups => {:user_id => self.id}})
end
end
Or if you're using an older version of Rails that doesn't have MetaWhere:
def all_trips
Trip.joins(:groups).where("(trips.user_id = ?) OR (groups.user_id = ?)", self.id, self.id)
end
I'm a beginner and it's hard to explain my problem:
My models:
class Skill
has_many :categories
has_many :positions, :through => :categories
end
class Category
belongs_to :skill
has_many :positions
end
class Position
belongs_to :category
has_one :skill, :through => :category
end
I can successfully eager load everything, like this:
#skills = Skill.includes(:positions)
However sometimes I want to apply a scope on the Positions:
class Position
...
scope :active, where(:hidden => false)
end
I wish I could do:
#skills = Skill.includes(:positions.active)
Instead, I apply the scope in the views, but the eager loading doesn't work anymore:
<%= skill.positions.acitve ... %>
Is it possible to have both eager loading and scope?
You could use another association:
class Skill
has_many :categories
has_many :positions, :through => :categories
has_many :active_positions, :through => :categories
end
class Category
belongs_to :skill
has_many :positions
has_many :active_positions, :class_name => "Position", :conditions => {:hidden => false}
end
class Position
belongs_to :category
has_one :skill, :through => :category
end
And then
#skills = Skill.includes(:active_positions)
But then you'll get two associations. If you ever use skill.positions, all the skill's positions will be loaded from the database. You should only use skill.active_positions.
Try this:
#skills = Skill.includes(:positions).where('position.active = TRUE')