Rails4覆盖has_many getter方法

时间:2016-11-16 10:08:26

标签: ruby-on-rails ruby-on-rails-4 override has-many eager-loading

我有一个Rails 4.1.7应用程序,我们有themesclass User < ActiveRecord::Base has_many :themes end class Theme < ActiveRecord::Base belongs_to :user end

user_id

现在,用户可以创建一些自己的主题(然后这些主题会将id设置为用户的user_id),或者可以使用任何预定义的主题(拥有{ {1}}设置为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 }),但它提供my ... WHERE user_id = 123 AND user_id IS NULL等mySql查询,返回空。我想使用Rails5我可以用has_many :themes, -> { or.where user_id: nil }之类的东西来做,但是现在不能改变Rails版本。

2 个答案:

答案 0 :(得分:1)

您可以将super(返回相关主题)与预定义的(user_idnil)结合使用:

class User < ActiveRecord::Base
  def themes
    Theme.where(id: super.ids + Theme.where(user_id: nil).ids)
  end
end

答案 1 :(得分:-1)

自从发布我的问题以来,我尝试了许多方法来实现我的目标。一个很有趣,我想,值得一提:

我尝试使用unscoperewhere解除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的评论使我和this回答相信,使用has_many进行查询是一回事,但它有其他方面,例如分配,并覆盖它为了一个人的利益可以破坏许多其他事情。

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

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

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