导轨:通过控制器通过记录更新现有has_many?



所以三分之二的工作。每次用户阅读文章时,都会创建(has_many通过)历史记录,该记录仅显示"用户在Read_Date_X阅读文章"。

数据库正常,模型正常,历史记录控制器中允许read_date参数,以下操作既有效 1) 检查用户以前是否阅读过文章,2) 创建新的历史记录(如果是第一次阅读本文)。

但是我无法弄清楚为什么中间位(只是更新现有记录上的read_date)不起作用。如果我用 h.save 尝试也没关系!或 h.update()。

h = History.where(article_id: @article, user_id: current_user)
if h.exists?
h = History.where(article_id: @article, user_id: current_user)
h.read_date = Time.now
h.save!
else
h = History.new
h.article_id = @article.id
h.user_id = current_user.id
h.read_date = Time.now
h.save!
end

如果找到现有记录,则引发的错误是:

undefined method `read_date=' for #<History::ActiveRecord_Relation:0x007fe7f30a5e50>

更新:工作答案

所以德里克是对的,这个版本有效。中间位需要一个实例,而不是一个数组,这是顶部条件(没有 .first)正在检查的内容。但是,使用它来返回单个记录意味着您需要在第二部分中将"存在?"交换为"现在?"。

h = History.where(article_id: @article, user_id: current_user).first
if h.present?
h.read_date = Time.now
h.save!
else
h = History.new
h.article_id = @article.id
h.user_id = current_user.id
h.read_date = Time.now
h.save!
end

History.where(article_id: @article, user_id: current_user)返回一个History::ActiveRecord_Relation。如果要设置read_date,则需要获取单个记录。

以下是您可以使用当前资源执行此操作的一种方法:

h = History.where(article_id: @article, user_id: current_user).first

处理此问题的另一种方法是使用find_by而不是where。这将返回单个记录。喜欢这个:

h = History.find_by(article_id: @article, user_id: current_user)

但是,如果用户有可能为一篇文章拥有许多历史记录,我会坚持您做事的方式并进行一次更改。如果由于某种原因您有很多历史记录,这可能不是很有效。

histories = History.where(article_id: @article, user_id: current_user)
histories.each { |history| history.update(read_date: Time.now) }

我意识到这个问题已经得到了回答。这里有一些额外的想法和建议。

  • 我不会有单独的read_date属性。只需改用updated_at即可。它已经在那里为你服务了。而且,您的代码的工作方式、read_dateupdated_at将始终(本质上)相同。

  • 在查找历史记录是否存在时,可以执行current_user.histories.where(article: @article)。IMO,这似乎比:History.where(article_id: @article, user_id: current_user).first更干净。

  • 您可以通过检查h分配是否成功来避免所有这些exists?present?业务。因此,if h = current_user.histories.where(article: @article).

  • 如果您走 使用updated_at而不是read_date的路线,那么您只需执行h.touch即可将updated_at设置为Time.now

  • 我会使用has_many :through提供的<<方法(而不是手动构建history记录)。同样,如果使用updated_at而不是read_date,则可以使用此方法。

因此,您可以将代码归结为:

if h = current_user.histories.where(article: @article)
h.touch 
else 
current_user.articles << @article 
end

您可以使用三元运算符而不是该if then else,在这种情况下,它可能看起来像:

current_user.histories.where(article: @article).tap do |h|
h ? h.touch : current_user.articles << @article 
end

最新更新