Rails:通过控制器通过记录更新现有的has_many?

时间:2017-12-30 18:03:01

标签: ruby-on-rails conditional updates has-many-through

因此,其中三分之二有效。每次用户阅读文章时,都会创建一个历史记录(has_many through),它只是说"用户在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>

更新:工作回答

所以Derek是对的,这个版本有效。中间位需要单个实例,而不是数组,这是顶级条件(没有.first)检查的内容。但是,使用它来返回单个记录意味着你需要交换&#34;存在?&#34;到&#34;出席?&#34;在第二部分。

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

2 个答案:

答案 0 :(得分:1)

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) }

答案 1 :(得分:1)

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

  • 我没有单独的read_date属性。只需使用updated_at代替。它已经在你身边了。而且,代码的工作方式,read_dateupdated_at将始终(基本上)相同。

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

  • 只需检查exists?分配是否成功,即可避免所有present?h业务。因此,if h = current_user.histories.where(article: @article)

  • 如果您选择使用updated_at代替read_date,则只需执行updated_at即可将Time.now设置为h.touch

  • 我会使用<<提供的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