azangru
azangru

Reputation: 2738

Rails: Class method scoping on the properties of an associated model

This is a somewhat more complicated version of the question I asked previously.

Background:

So what I need is to display a list of articles. An article belongs to a media outlet. A media is located in a particular country and publishes articles in a particular language. So the data structure is as follows:

Now, if I wanted to filter articles by media, I could use the following class method (I prefer class methods over scopes, because I am passing a parameter and am using a conditional statement inside the method):

  def self.filter_by_media(parameter)
    if parameter == "all"
      all
    else
      where(media_id: parameter)
    end
  end

Question:

How to write a class method that would filter Articles based by properties of its associated model, the Media? For example, I want to get a list of articles published by media located a certain counrty or in several countries (there is also a default country when the user does not make any choice). Here’s what I tried:

  # parameter can be either string 'default' or an array of id’s

  def self.filter_by_country(parameter)
    if parameter == "default"
      joins(:media).where(media: [country_id: 1])
    else
      joins(:media).where(media: [country_id: parameter])
    end
  end

But that doesn’t work, and I am not conversant enough with SQL to figure out how to make this work. Could you please help?

Update:

I’m trying out @carlosramireziii's suggestion. I changed arrays into hashes (don't know what possessed me to use arrays in the first place), but I’m getting the following error in the Rails console (to avoid confusion, in my database, media is called agency):

  def self.filter_by_country(parameter)
    if parameter == "default"
      joins(:agency).where(agency: {country_id: 1})
    else
      joins(:agency).where(agency: {country_id: parameter})
    end
  end

in Rails console:
> Article.filter_by_country('default')

=> Article Load (1.9ms)  SELECT "articles".* FROM "articles" INNER JOIN "agencies" ON "agencies"."id" = "articles"."agency_id" WHERE "agency"."country_id" = 1
PG::UndefinedTable: ERROR:  missing FROM-clause entry for table "agency"
LINE 1: ...ON "agencies"."id" = "articles"."agency_id" WHERE "agency"."...
                                                             ^
: SELECT "articles".* FROM "articles" INNER JOIN "agencies" ON "agencies"."id" = "articles"."agency_id" WHERE "agency"."country_id" = 1

Update 2

My mistake in the Update section above is that I did not pluralize agency in the where clause. The part where(agency: {country_id: 1}) should have read where(agencies: {country_id: 1}). The pluralized word agencies here refers to the name of the table that is being joined.

Upvotes: 0

Views: 58

Answers (1)

Carlos Ramirez III
Carlos Ramirez III

Reputation: 7434

You are very close, you just need to use a nested hash instead of an array.

Try this

def self.filter_by_country(parameter)
   if parameter == "default"
      joins(:media).where(media: { country_id: 1 })
   else
      joins(:media).where(media: { country_id: parameter })
   end
end

Upvotes: 2

Related Questions