0

我有3节课。国家、城市和活动。一个国家有_许多城市,一个城市有_许多活动。

一些活动尚未准备好,因此这些活动处于审批状态。我想设置范围,所以没有批准活动的城市是无效的,没有有效城市的国家也是无效的。

class ApprovalStatus < ActiveRecord::Base
  def self.approved_status_id
    ApprovalStatus.find_or_create_by(name: 'Approved').id
  end 

class Activity < ActiveRecord::Base
  # Named scopes
  scope :that_is_approved, -> { where approval_status_id: ApprovalStatus.approved_status_id }

class City < ActiveRecord::Base
  # Named scopes
  scope :that_has_valid_activities, -> { joins(:activities).merge(Activity.that_is_approved).uniq }

class Country < ActiveRecord::Base
  # Named scopes
  scope :that_has_valid_cities, -> { joins(:cities).uniq }

  # Associations
  has_many :cities, -> { that_has_valid_activities }, dependent: :destroy

我希望范围和关联,因为我希望 Country.that_has_valid_cities 仅返回有效国家,并且当活动模型序列化程序获取城市时,仅在这些有效国家中显示有效城市。

在 rails 控制台中: City.that_has_valid _activities 运行良好。

ApprovalStatus Load (13.4ms)  SELECT  "approval_statuses".* FROM "approval_statuses" WHERE "approval_statuses"."name" = $1 LIMIT 1  [["name", "Approved"]]
City Load (21.6ms) SELECT DISTINCT "cities".* FROM "cities" INNER JOIN "activities" ON "activities"."city_id" = "cities"."id" WHERE "activities"."approval_status_id" = $1  [["approval_status_id", 2]]

Country.that_has_valid_cities 中断。

ApprovalStatus Load (0.4ms)  SELECT  "approval_statuses".* FROM "approval_statuses" WHERE "approval_statuses"."name" = $1 LIMIT 1  [["name", "Approved"]]
ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR:  missing FROM-clause entry for table "activities" LINE 1: ..." ON "cities"."country_id" = "countries"."id" AND "activitie...
                                                         ^
: SELECT DISTINCT "countries".* FROM "countries" INNER JOIN "cities" ON "cities"."country_id" = "countries"."id" AND "activities"."approval_status_id" = $1

当我希望它进行第二次 INNER JOIN 时,它似乎正在尝试执行 AND。

class Country < ActiveRecord::Base

  # Named scopes
  scope :that_has_valid_cities, -> { joins(:cities).merge(City.that_has_valid_activities).uniq }

  # Associations
  has_many :cities, dependent: :destroy

工作并产生查询:

  ApprovalStatus Load (0.6ms)  SELECT  "approval_statuses".* FROM "approval_statuses" WHERE "approval_statuses"."name" = $1 LIMIT 1  [["name", "Approved"]]
  Country Load (1.2ms)  SELECT  DISTINCT "countries".* FROM "countries" INNER JOIN "cities" ON "cities"."country_id" = "countries"."id" LEFT OUTER JOIN "activities" ON "activities"."city_id" = "cities"."id" WHERE "activities"."approval_status_id" = $1  ORDER BY "countries"."id" ASC LIMIT 1  [["approval_status_id", 2]]

但是,返回的国家/地区仍会列出其无效城市,因为无效城市并未从有效国家/地区中过滤出来。

PS让我知道整个风格是否在这里关闭,因为我正在自己学习rails。

4

2 回答 2

0
# app/models/approval_status.rb
class ApprovalStatus < ActiveRecord::Base
  has_many :activities

  def self.approved_status
    # I added some caching
    return @approved_status if @approved_status
    @approved_status = ApprovalStatus.find_or_create_by(name: 'Approved')
  end
end


# app/models/activity.rb
class Activity < ActiveRecord::Base
  belongs_to :city
  belongs_to :approval_status

  scope :that_is_approved, -> { where approval_status: ApprovalStatus.approved_status }
end


# app/models/city.rb
class City < ActiveRecord::Base
  belongs_to :country
  has_many :activities

  scope :that_has_valid_activities, -> { joins(:activities).where(activities: {id: Activity.that_is_approved}) }
end


# app/models/country.rb
class Country < ActiveRecord::Base
  has_many :cities, -> { that_has_valid_activities }, dependent: :destroy
  # I added this (probably would help you along the way in case you do not know yet about :through)
  has_many :activities, through: :cities

  scope :that_has_cities_having_valid_activities, -> { joins(cities: :activities) }
end

用法

Country.that_has_cities_having_valid_activities
City.that_has_valid_activities

明白了

  • 请注意

    Country.joins(:cities)将不再工作,并会产生和错误。你需要它总是:activities像这样加入: Country.joins(cities: :activities)

    • 这是因为您has_many :cities, -> { that_has_valid_activities }正在查询关联的:activities. 找不到自动完成这项工作的方法。

推荐

  • 我建议不要使用

    has_many :cities, -> { that_has_valid_activities }
    scope :that_has_cities_having_valid_activities, -> { joins(cities: :activities) }
    

    而是使用:

    has_many :cities
    scope :that_has_cities_having_valid_activities, -> { joins(:cities).where(cities: {id: City.that_has_valid_activities} ) }
    
    • 这样您仍然可以查询“无效”城市。否则,@country.cities将始终使用“有效”城市进行过滤,并且未来可能会查询“无效”城市的情况将很棘手,并且可能涉及一些丑陋的代码。

    • 这也将解决上面的问题

测试工作

于 2017-07-14T15:37:39.263 回答
0

我想你可以检查一下相反的情况。检查是否有任何活动城市国家无效。你只需要运行一个find_by. 如果它返回nil,那么你就是黄金,如果它返回一个对象,那么它是无效的。

好滚环

于 2017-07-14T15:35:20.300 回答