覆盖默认访问器时更新属性的问题

Posted

技术标签:

【中文标题】覆盖默认访问器时更新属性的问题【英文标题】:Trouble on updating attributes when default accessors are overwritten 【发布时间】:2014-04-09 23:18:36 【问题描述】:

我正在使用 Ruby on Rails 4,并以这种方式覆盖了一些默认访问器方法:

class Article < ActiveRecord::Base
  def title
    self.get_title
  end

  def content
    self.get_content
  end
end

self.get_titleself.get_content 方法返回一些计算值,如下所示(注意:has_one_association:has_one ActiveRecord::Association

def get_title
  self.has_one_association.title.presence || read_attribute(:title)
end

def get_content
  self.has_one_association.content.presence || read_attribute(:content)
end

当我从数据库中找到并读取 @article 实例时,所有实例都按预期工作:titlecontent 值分别使用 self.has_one_association.titleself.has_one_association.content 输出。

但是,我发现当属性分配给 @article 时,@article 对象没有按预期更新。也就是说,在我的控制器中,我有:

def update
  # params # => :article => :title => "New title", :content => "New content")

  ...

  # BEFORE UPDATING
  # @article.title   # => "Old title"   # Note: "Old title" come from the 'get_title' method since the 'title' accessor implementation
  # @article.content # => "Old content" # Note: "Old content" come from the 'get_content' method since the 'content' accessor implementation

  if @article.update_attributes(article_params)

    # AFTER UPDATING
    # @article.title   # => "Old title"
    # @article.content # => "Old content"

    ...
  end
end

def article_params
  params.require(:article).permit(:title, :content)
end

即使 @article 有效,它也没有在数据库中更新(!),我认为是因为我覆盖访问器的方式和/或 Rails 的方式 assign_attributes。当然,如果我删除 getter 方法,那么一切都会按预期工作。

这是一个错误吗?我该如何解决这个问题?或者,我应该采用另一种方法来完成我想要完成的事情吗?


另见https://github.com/rails/rails/issues/14307

【问题讨论】:

【参考方案1】:

update_attributes 在这种情况下只是调用title=content=save 的快捷方式。如果你没有覆盖 setter,只是 getter,它是不相关的。

您正在正确更新值,但 Rails 没有读取您设置的值,因为它覆盖了要从关联中读取的 getter。您可以通过检查@article.attributes 或查看数据库中的文章记录来验证这一点。

另外,您的get_content 正在尝试使用read_attribute(:title) 而不是:content

【讨论】:

"[...] 您的 get_content 正在尝试读取_attribute(:title) 而不是 :content。"是我的错字,所以我更新了问题。但是,如果我以 def title=(value); write_attribute(:title, value); enddef content=(value); write_attribute(:content, value); end 的这种方式覆盖 setter 方法,那么它 仍然 无法按预期工作。 P.S.:可能我没明白你回答的意义何在。 我认为你没有。不要覆盖设置器。属性的更新工作正常,它们的回读(由于您覆盖了 getter)是错误所在。 我仔细检查了@article.attributes (# =&gt; #&lt;Article id: 1 :title =&gt; "Old title" :content =&gt; "Old content"&gt;) 和数据库,更新后没有任何变化。任务似乎没有按预期工作。

以上是关于覆盖默认访问器时更新属性的问题的主要内容,如果未能解决你的问题,请参考以下文章

为自定义控件实现值访问器时,未从事件中的模型获取更新值

使用子解析器时,命令行参数被默认值覆盖

Core Data 无法生成原始访问器

如何在午睡中覆盖某些资源的默认(“**”)装饰器?

如何覆盖会话属性?

属性类型与访问器类型不匹配(子类化时)