Scopes, lambda and rails 3

2019-08-22 22:59发布

问题:

having a play with scopes tonight in rails 3, and trying to get my head around what lambda does?

What I am trying to achieve in this example is get a list of country names (:name) from my countries model which is associated with my recipe model. A recipe belongs_to country and a country has many recipes.

I would like to order the recipes by amount of times they appear in a recipe, starting with the highest..

So i am trying this in my recipe model ( or should I do it within the country model?, but then that wouldn’t work would it as my country model is pre populated with 1 instance of every country in the world)

scope :top_countries, lambda { joins(:countries).merge(Country.name).order("name DESC") }

however i get this error message

undefined method `default_scoped?' for "Country":String

my controller

@toprankingcountry = Recipe.top_countries

obviously my understanding is not what i thought and would appreciate some pointers/assistance

Thanks

回答1:

I believe the problem is with merge(Country.name) part. joins(:countries) returns ActiveRecord::Relation instance. Its merge method expects one argument which is another instance of a ActievRecord::Relation, whereas you merge string Country.name.

Generally speaking, getting a list of top countries effectively means getting a list of Countries order by some additional condition. So I'd put this logic into a Country model.

class Country
  has_many :recipes

  def self.top_countries
    joins(:recipes).
      select('countries.*, count(*) AS recipes_count').
      group('countries.id').
      order('recipes_count DESC')
  end
end

Also if you are using RDBMS which cannot figure out dependent rows on its own (like PostgreSQL < 9.1) you'll have to manually list all the columns in group by clause.

get a list of country names (:name) from my countries model which is associated with my recipe model (instance)

I believe it is impossible to achieve what you described as is because a country has_many recipes, not vice versa