To nie jest dokładnie pytanie, raczej raport o tym, jak rozwiązałem problem z write_attribute
, gdy atrybut jest obiektem, na Railsach 'Active Record
. Mam nadzieję, że może to być użyteczne dla innych osób borykających się z tym samym problemem.Problem z korektorem ustawiającym w ActiveRecord
Pozwolę sobie wyjaśnić na przykładzie. Załóżmy, że masz dwie klasy, Book
i Author
:
class Book < ActiveRecord::Base
belongs_to :author
end
class Author < ActiveRecord::Base
has_many :books
end
bardzo proste. Ale z jakiegokolwiek powodu musisz zastąpić metodę author
= na Book
. Ponieważ jestem nowy w Rails, postępowałem zgodnie z sugestią Sam Ruby dotyczącą Agile Web Development z Railsami: używaj prywatnej metody attribute_writer
. Tak więc, moja pierwsza próba to:
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
Niestety, to nie działa. To, co otrzymuję od konsoli:
>> 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
Wydaje się, że Rails nie rozpoznaje to jest obiekt i robi nic: po attribuition, autor nadal jest zerowa! Oczywiście, mógłbym spróbować write_attribute(:author_id, author.id)
, ale to nie pomaga, gdy autor nie jest jeszcze zapisany (wciąż nie ma id!) I potrzebuję, aby obiekty były zapisywane razem (autor musi być zapisany tylko jeśli książka jest ważna).
Po szukać dużo rozwiązania (i spróbować wielu innych rzeczy na próżno), znalazłem tę wiadomość: http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/4fe057494c6e23e8, więc w końcu mogę miał jakiś działający kod:
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
tym czasie, konsola była miło mi:
>> 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>
Sztuką jest tu alias_method_chain
, że tworzy przechwytywania (w tym przypadku author_with_lookup
) i alternatywnej nazwy do starego seter (author_without_lookup
). Przyznaję, że zajęło to trochę czasu, aby zrozumieć tę aranżację i byłbym zadowolony, gdyby ktoś chciał wyjaśnić to szczegółowo, ale zaskoczyło mnie to, że nie ma informacji na temat tego rodzaju problemu. Muszę dużo google znaleźć tylko jeden post, który według tytułu wydawał początkowo niezwiązany z problemem. Jestem nowy w Rails, więc jak myślisz: czy to jest zła praktyka?
myślałem, aby to, ale nie chciałem zduplikowane atrybuty. Metoda, którą zaproponowałem powyżej, działa bardzo dobrze; Po prostu chciałem się nim podzielić. Rzeczywiście mogę zrobić z nim sztuczkę text_field. Ale dzięki za odpowiedź! = D –
Czy nie byłoby lepiej zastąpić metodę 'author_name' przez' delegate: name,: to =>: author,: prefix => true'? –
@Adam, To z pewnością jest alternatywny sposób na zrobienie tego. Zwykle używam tylko 'delegate', gdy mamy do czynienia z wieloma metodami. Jeśli jest tylko jeden, wolę bezpośrednio zdefiniować metodę, ponieważ uważam, że jest bardziej przejrzysta. – ryanb