Rails4 覆盖 has_many getter 方法

Rails4 override has_many getter method

我有一个 Rails 4.1.7 应用程序,其中有 usersthemes:

class User < ActiveRecord::Base
  has_many :themes
end

class Theme < ActiveRecord::Base
  belongs_to :user
end

现在,用户可以创建一些自己的主题(然后这些主题将 user_id 设置为用户的 id),或者可以使用任何预定义的主题(具有 user_id 设置为 null)

我想做的是:以某种方式更改 has_many 关联,这样当我调用 @user.themes 时,这会给我带来预定义的主题以及用户的主题。

我尝试过的:

1) 定义实例方法而不是has_many关联:

class User < ActiveRecord::Base
  def themes
    Theme.where user_id: [id, nil]
  end
end

但由于我想与用户 (includes(:themes)) 一起急切加载主题,所以这实际上是行不通的。

2) 使用一些范围 (has_many :themes, -> { where user_id: nil }),但它给出 mySql 查询,如 ... WHERE user_id = 123 AND user_id IS NULL,其中 returns 为空。我猜 Rails5 我可以用 has_many :themes, -> { or.where user_id: nil } 之类的东西来做,但是现在不能更改 Rails 版本。

自从发布我的问题后,我尝试了很多事情来实现我的目标。其中一个很有趣,我想值得一提:

我尝试使用 unscope or rewhere 取消 has_many 关联的范围,它看起来像这样:

has_many :themes, -> user = self { # EDIT: `= self` can even be omitted
  u_id = user.respond_to?(:id) ? user.id : user.ids

  unscope(where: :user_id).where(user_id: [u_id, nil])
  # or the same but with other syntax:
  rewhere(user_id: [u_id, nil])
}

当我尝试 @user.themes 时,效果非常好,并给出了以下 mySql 行:

SELECT `themes`.* FROM `themes`
       WHERE ((`themes`.`user_id` = 123 OR `themes`.`user_id` IS NULL))

但是当我尝试加载它时(毕竟我开始研究的原因),它只是拒绝取消查询的范围,并给出相同的旧 user_id = 123 AND user_id = NULL 行。

毕竟,@Ilya 的评论和 的回答一起说服了我,使用 has_many 进行查询是一回事,但它还有其他方面,例如分配,和为了一个人的缘故而覆盖它可能会破坏许多其他东西。

所以我决定继续使用我的好方法,只是我给它起了一个更具体的名称以避免将来混淆:

def available_themes
  Theme.where user_id: [id, nil]
end

至于@AndreyDeineko 的回应——因为他一直拒绝回答我的问题,总是回答一些从未被问过的问题——我仍然不明白为什么他的方法(与我的 available_themes 的结果相同) ,但使用 3 个额外的查询)将是更好的解决方案。