sploiber
sploiber

Reputation: 621

Rails 3 multi-level join

I have been reading to understand the best Rails 3 way to do the following. I'd be very grateful for suggested approaches. (scopes?)

I have the following models:

class DonorCategory < ActiveRecord::Base
  has_many :donors
end

class Donor < ActiveRecord::Base
  has_many :donations
  belongs_to :donor_category
end

class Donation < ActiveRecord::Base
  belongs_to :donor
end

what I need is this: "All donations associated with a given donor category, for given dates" The date criteria is applied to Donation, but the donor category criterion is applied to Donor So it is as if I need to filter the Donors which are applied to the Donation query.

Upvotes: 1

Views: 800

Answers (2)

Rob Davis
Rob Davis

Reputation: 15772

You could add a helpful query method to Donor. And adding a has_many ... through on DonationCategory would give you easy access to the Donations from a given category, automatically joining the tables, like this:

class DonationCategory < ActiveRecord::Base
    has_many :donors
    has_many :donations, through: :donors
end

class Donation < ActiveRecord::Base

  def self.within_dates(start_date, end_date)
    where "created_at >= ? AND created_at <= ?", start_date, end_date
  end

end

# Query looks like this:
some_category.donations.within_dates(start_date, end_date)

# Or this:
DonorCategory.find(123).donations.within_dates(start_date, end_date)

To use the through option on has_many, you don't need to modify your database at all. Rails will get the donations from the donor_category's donors, by joining your donations, donors, and donor_categories tables.

You mentioned scopes. The within_dates class method is effectively a scope. scope is just special rails syntax for creating a class method that queries the database. It's a redundant mechanism, but DHH likes it. And I agree that often a scope is easier on the eyes than the equivalent class method, but when the scope requires arguments, as it would here, I think the class method is actually more straightforward.

UPDATE

RFC1337's answer makes me realize the query method could be simplified:

def self.within_dates(start_date, end_date)
  where created_at: start_date..end_date
end

Upvotes: 2

RFC1337
RFC1337

Reputation: 167

I think what you need is something like this:

Donor.where(:donor_category => DonorCategory.find(id)).where(:date => start_date..end_date)

Upvotes: 0

Related Questions