ruby-on-rails - ActiveRecord 上的 setter 覆盖问题

标签 ruby-on-rails activerecord

这不完全是一个问题,而是关于我如何解决 write_attribute 问题的报告当属性是对象时,在 Rails 的 Active Record 上.我希望这对面临同样问题的其他人有用。

让我用一个例子来解释。假设你有两个类,BookAuthor :

class Book < ActiveRecord::Base
  belongs_to :author
end

class Author < ActiveRecord::Base
  has_many :books
end

非常简单。但是,无论出于何种原因,您都需要覆盖 author = 方法 Book .由于我是 Rails 新手,所以我遵循了 Sam Ruby 关于使用 Rails 进行敏捷 Web 开发的建议:使用 attribute_writer私有(private)方法。所以,我的第一次尝试是:
class Book < ActiveRecord::Base
  belongs_to :author

  def author=(author)
    author = Author.find_or_initialize_by_name(author) if author.is_a? String
    self.write_attribute(:author, author)
  end
end

不幸的是,这不起作用。这就是我从控制台得到的:
>> book = Book.new(:name => "Alice's Adventures in Wonderland", :pub_year => 1865)
=> #<Book id: nil, name: "Alice's Adventures in Wonderland", pub_year: 1865, author_id: nil, created_at: nil, updated_at: nil>
>> book.author = "Lewis Carroll"
=> "Lewis Carroll"
>> book
=> #<Book id: nil, name: "Alice's Adventures in Wonderland", pub_year: 1865, author_id: nil, created_at: nil, updated_at: nil>
>> book.author
=> nil

似乎 Rails 不承认它是一个对象并且什么都不做:在属性之后,作者仍然是 nil!当然,我可以试试write_attribute(:author_id, author.id) ,但是当作者尚未保存时(它仍然没有 id!)并且我需要将对象一起保存(仅当书籍有效时必须保存作者)时,它没有帮助。

在搜索了很多解决方案(并徒劳地尝试了许多其他事情)之后,我发现了这条消息:http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/4fe057494c6e23e8 ,所以最后我可以有一些工作代码:
class Book < ActiveRecord::Base
  belongs_to :author

  def author_with_lookup=(author)
    author = Author.find_or_initialize_by_name(author) if author.is_a? String
    self.author_without_lookup = author
  end
  alias_method_chain :author=, :lookup
end

这一次,控制台对我很好:
>> book = Book.new(:name => "Alice's Adventures in Wonderland", :pub_year => 1865)
=> #<Book id: nil, name: "Alice's Adventures in Wonderland", pub_year: 1865, author_id: nil, created_at: nil, updated_at: nil>
>> book.author = "Lewis Carroll"=> "Lewis Carroll"
>> book
=> #<Book id: nil, name: "Alice's Adventures in Wonderland", pub_year: 1865, author_id: nil, created_at: nil, updated_at: nil>
>> book.author
=> #<Author id: nil, name: "Lewis Carroll", created_at: nil, updated_at: nil>

这里的诀窍是alias_method_chain ,它创建一个拦截器(在本例中为 author_with_lookup )和旧 setter 的替代名称( author_without_lookup )。我承认花了一些时间来理解这种安排,如果有人愿意详细解释它,我会很高兴,但令我惊讶的是缺乏关于这类问题的信息。我必须用谷歌搜索很多帖子才能找到一篇文章,标题似乎最初与问题无关。我是 Rails 的新手,所以你们觉得伙计们:这是一种不好的做法吗?

最佳答案

我建议创建一个虚拟属性而不是覆盖 author=方法。

class Book < ActiveRecord::Base
  belongs_to :author

  def author_name=(author_name)
    self.author = Author.find_or_initialize_by_name(author_name)
  end

  def author_name
    author.name if author
  end
end

然后你可以做一些很酷的事情,比如将它应用到表单域。
<%= f.text_field :author_name %>

这对你的情况有用吗?

关于ruby-on-rails - ActiveRecord 上的 setter 覆盖问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1283046/

相关文章:

ruby-on-rails - Rails - 通过参数传递多个复选框值

php - Codeigniter:将 activeRecord 与手动查询相结合?

ruby-on-rails - 比较 2 个大型 ActiveRecord 关系进行排序

ruby-on-rails - Rails : Could not find rake-10. 1.1 在任何来源

ruby-on-rails - 在 Postgres JSONB 列中按数字排序

mysql - Rails ActiveRecord - 有没有办法在没有 id 的情况下对表执行操作?

ruby-on-rails - ActiveRecord 自定义类型命名方案

ruby-on-rails - 我如何返回这个 ruby 数组

Mysql2::错误:表.schema_migrations'不存在

ruby-on-rails - Ruby on Rails : building a model with an attribute not in the database?