10

我在 RoR 中有一个简单的模型,我想保留人们在网站上输入的所有内容。但是,如果用户单击“删除”,我也希望能够隐藏一些内容。

所以我在我的模型中添加了一个名为“displayed”的布尔属性。

我想知道,最佳实践风格的方法是什么。

我想我必须用类似的东西改变控制器:

def destroy
 @point = Point.find(params[:id])
 @point.displayed = false
 @point.save

respond_to do |format|
  format.html { redirect_to points_url }
  format.json { head :no_content }
end

但我不确定它是否干净。最好的方法是什么。

正如你猜想的那样,我对 RoR 不屑一顾。代码块将不胜感激。

谢谢

4

5 回答 5

13

自己实现它(而不是使用 gem)。它比一开始看起来要容易得多,而且它也没有任何改变方法含义的宝石那么复杂destroy,在我看来,这是一个坏主意。

我并不是说使用 gem 本身很复杂——我是说通过改变destroy方法的含义,你正在改变 Rails 世界中人们认为理所当然的东西的含义——当你调用destroy该记录时会消失,destroy如果它们通过dependent: destroy回调链接在一起,也可能会在依赖对象上调用。

更改 的含义destroy也很糟糕,因为在“约定优于配置”的世界中,当您违反约定时,您实际上是在破坏 Rails 代码的“自动性”。所有这些你认为理所当然的东西,因为你阅读了一段 Rails 代码,并且你知道某些假设通常适用——那些已经过时了。当您以不明显的方式更改这些假设时,您几乎肯定会因此而引入一个错误。

不要误会我的意思,没有什么比实际阅读代码来检查您的假设更好的了,但作为一个社区,能够谈论某些事情并通常让他们的行为以某种方式行事也很好。

考虑以下:

  • Rails 中没有规定必须destroy在控制器中实现动作,所以不要这样做。这是标准操作之一,但不是必需的。
  • 使用该update操作设置和清除archived布尔属性(或类似名称)
  • 我使用了acts_as_paranoidgem,如果您需要在模型中添加任何范围(除了 gem 提供的范围),您将发现自己不得不绕过它,关闭默认的“隐藏存档记录” " 范围,当你遇到它时,它几乎立即失去了它的价值。此外,该 gem 本身几乎什么都不做,而且它的功能可以很容易地自己编写(我的意思是只比安装 gem 本身做更多的工作),所以从这个角度来看使用它确实没有任何好处。
  • 如前所述,重写方法或动作是一个坏主意,因为它打破了 Rails(和 ActiveRecord)关于调用对象destroy意味着什么的约定。destroy任何这样做的宝石(acts_as_paranoid例如)也打破了这个惯例,你最终会混淆自己或其他人,因为destroy根本不会意味着它应该意味着什么。这会给您的代码增加混乱,而不是清晰。不要这样做——你以后会付钱的。
  • 如果您想使用软删除 gem,因为您要防止一些理论上的、未来的开发人员可能会占用您的数据……那么,最好的解决方案是不要雇用这些人或与这些人一起工作。没有经验的人需要指导,而不是防止他们犯错的宝石。
  • 如果你真的,绝对,必须防止破坏给定模型的记录(除了能够简单地归档它),然后使用before_destroy回调并简单地返回 false,这将防止它被破坏,除非显式调用delete被使用(无论如何都与破坏不同)。此外,有了回调就可以(a)很明显为什么destroy不改变其含义就不能工作,并且(b)很容易编写测试以确保它不可破坏。这意味着将来,如果您不小心删除了该回调或执行其他使该模型可销毁的操作,则测试将失败,并提醒您注意这种情况。
于 2013-02-06T01:27:08.403 回答
11

像这样的东西:

class Point < ActiveRecord::Base

  def archive        
    update_attribute!(:displayed, false)
  end 

end

然后@point.archive在您通常调用的控制器的销毁操作中调用@point.destroy. 您还可以创建一个隐藏存档点,直到您明确查询它们,请参阅应用默认范围的default_scopeRoR 指南。

编辑:根据下面的 normalocity 和 logan 的评论更新了我的答案。

于 2013-02-05T23:20:17.590 回答
2

查看acts_as_archive gem。它将无缝地进行软删除。

于 2013-02-05T23:16:00.960 回答
2

您的解决方案很好,但您可以使用acts_as_paranoid gem来管理它。

于 2013-02-05T23:16:16.817 回答
0

在这种情况下,与其添加一个新的布尔标志,不如添加一个deleted_at:datetime

@point = Point.find(params[:id])
@point.touch(:deleted_at)
...

然后稍后

Point.where(deleted_at: nil) # these are NOT hidden
Point.where.not(deleted_at: nil) # these are hidden
于 2019-04-26T16:57:55.180 回答