RoR - 不要破坏对象,只是标记为隐藏

时间:2013-02-05 23:12:18

标签: ruby-on-rails

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

所以我在模型中添加了一个名为“显示”的bolean属性。

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

我想我必须用以下内容更改控制器:

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很吵。大块的代码将不胜感激。

谢谢

5 个答案:

答案 0 :(得分:13)

自己实施(而不是使用宝石)。这比起初看起来要容易得多,并且它比那些改变 destroy方法的含义要简单得多,这在我看来是个坏主意意见。

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

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

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

请考虑以下事项:

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

答案 1 :(得分:11)

这样的事情:

class Point < ActiveRecord::Base

  def archive        
    update_attribute!(:displayed, false)
  end 

end

然后在控制器的销毁操作中调用@point.archive,您通常会调用@point.destroy。您还可以创建default_scope隐藏已归档的点,直到您明确查询它们为止,参见appling a default scope上的RoR指南。

编辑:根据normalocity&amp;更新我的答案logan的评论如下。

答案 2 :(得分:2)

看看acts_as_archive宝石。它将无缝地进行软删除。

答案 3 :(得分:2)

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

答案 4 :(得分: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