Rails4 覆盖 has_many getter 方法
Rails4 override has_many getter method
我有一个 Rails 4.1.7 应用程序,其中有 users
和 themes
:
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 个额外的查询)将是更好的解决方案。
我有一个 Rails 4.1.7 应用程序,其中有 users
和 themes
:
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 个额外的查询)将是更好的解决方案。