【发布时间】:2009-08-15 22:42:17
【问题描述】:
这不完全是一个问题,而是关于当属性是对象时我如何解决 write_attribute 问题的报告,在 Rails 的 Active Record 上。我希望这对面临同样问题的其他人有用。
让我用一个例子来解释。假设你有两个类,Book 和 Author:
class Book < ActiveRecord::Base
belongs_to :author
end
class Author < ActiveRecord::Base
has_many :books
end
非常简单。但是,无论出于何种原因,您都需要覆盖Book 上的author= 方法。由于我是 Rails 新手,因此我遵循了 Sam Ruby 关于使用 Rails 进行敏捷 Web 开发的建议:使用 attribute_writer 私有方法。所以,我的第一次尝试是:
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)和旧设置器的替代名称(author_without_lookup)。我承认花了一些时间来理解这种安排,如果有人愿意详细解释它,我会很高兴,但令我惊讶的是缺乏关于这类问题的信息。我必须用谷歌搜索很多才能找到一篇文章,标题似乎最初与问题无关。我是 Rails 的新手,你们怎么看:这是一种不好的做法吗?
【问题讨论】:
标签: ruby-on-rails activerecord