Como salvar um registro associando entre tem-muitas classes em Ruby on Rails
-
16-09-2019 - |
Pergunta
Eu criei três classes para representar Livros , Pessoas e BookLoans . Enquanto eu sou capaz de mostrar a associação de pessoas para Livros através BookLoans eu estive semeando meu banco de dados.
Agora eu preciso para salvar um check-out de um livro. Era minha intenção de fazer esta ação através do controlador livro. Especificamente, criando uma ação empréstimo no BooksController. Enquanto isso faz sentido para mim, em teoria, eu estou tendo um momento terrível implementar a sintaxe apropriada.
Eu adicionei a capacidade de emprestar um livro a partir do show ponto de vista de um livro. Este ponto de vista agora contém um formulário que usa a ação empréstimo do controlador livro para registrar o empréstimo.
Eu adicionei o que eu acredito que são os métodos apropriados para o meu modelo Book. Com a ajuda de theIV eu ter capturado as informações apropriadas no controlador. Infelizmente, quando eu pressionar empréstimo no show livro visualizar um registro book_loan não está sendo gravada.
O que eu estou ausente?
Livro Modelo
class Book < ActiveRecord::Base
has_many :book_loans
has_many :borrowers, :through => :book_loans, :source => :person
accepts_nested_attributes_for :book_loans, :allow_destroy => true
def loaned?
book_loans.exists?(:return_date => nil)
end
def current_borrower
if loaned?
book_loans.first(:order => "out_date desc").person
end
end
def add_loan (person_id)
book_loans.create(:book_id => id,
:person_id => person_id,
:out_date => Date.current)
end
end
Método Empréstimo do BooksController
def loan
@book.add_loan(params[:book_loan][:person_id])
redirect_to :action => 'book', :id => params[:id]
end
Livro Show View w forma de empréstimo /
<p>
<b>Title:</b>
<%=h @book.title %>
</p>
<p>
<b>Isbn10:</b>
<%=h @book.isbn10 %>
</p>
<p>
Currently loaned to:
<%=h borrower_name(@book) %>
</p>
<% form_for(@book) do |x| %>
<p>
<%= x.label :loan_person_id %><br/>
<%= collection_select(:book_loan, :person_id,
Person.find(:all, :order => 'name ASC'), :id, :name) %>
<%= x.submit 'Loan', :action => 'loan' %>
</p>
<% end %>
BookLoan Modelo
class BookLoan < ActiveRecord::Base
belongs_to :book
belongs_to :person
end
Pessoa Modelo
class Person < ActiveRecord::Base
has_many :book_loans
has_many :books, :through => :book_loans
end
Log Desenvolvimento
Processing BooksController#update (for 127.0.0.1 at 2009-09-24 13:43:05) [PUT]
Parameters: {"commit"=>"Loan", "authenticity_token"=>"XskHLuco7Q7aoEnDfVIiYwVrMEh5uwidvJZdrMbYYWs=", "id"=>"1", "book_loan"=>{"person_id"=>"3"}}
[4;35;1mBook Columns (3.0ms)[0m [0mSHOW FIELDS FROM `books`[0m
[4;36;1mBook Load (4.0ms)[0m [0;1mSELECT * FROM `books` WHERE (`books`.`id` = 1) [0m
[4;35;1mSQL (0.0ms)[0m [0mBEGIN[0m
[4;36;1mBook Load (1.0ms)[0m [0;1mSELECT `books`.id FROM `books` WHERE (`books`.`title` = BINARY 'Dreaming in Code: Two Dozen Programmers, Three Years, 4,732 Bugs, and One Quest for Transcendent Software' AND `books`.id <> 1) LIMIT 1[0m
[4;35;1mSQL (1.0ms)[0m [0mCOMMIT[0m
Redirected to http://localhost:3000/books/1
Completed in 19ms (DB: 10) | 302 Found [http://localhost/books/1]
[4;36;1mSQL (0.0ms)[0m [0;1mSET NAMES 'utf8'[0m
[4;35;1mSQL (0.0ms)[0m [0mSET SQL_AUTO_IS_NULL=0[0m
Solução
Quando você has_many
um modelo, você tem acesso a alguns métodos extra, duas em particular, são collection.build
e collection.create
build
- é como new
, create
é como create
:). Dê uma olhada na tem muitas documentação . Neste caso, você poderia reescrever add_loan como
def add_loan (person_id)
book_loans.create(:book_id => id,
:person_id => person_id,
:out_date => Date.current)
end
ou algo similar.
Para responder à sua pergunta sobre o que a visão será enviando para o controlador, ele irá enviar o hash params
como de costume, mas se você só quer passar o person_id
para add_loan
, você pode extrair isso. Assumindo que a parte da visão você tem acima é envolto em um formulário para um empréstimo de livro, você pode acessar a pessoa por params[:book_loan][:person_id]
. Você também vai querer fazer um achado em que a ação em primeiro lugar, ou então @book
vai ser nulo.
Espero que isso ajude. Felicidades.
EDIT: Eu não tenho certeza se a maneira de tê-lo agora funciona, mas eu acho que você quer mudar seu modelo Person
para ler
class Person < ActiveRecord::Base
has_many :book_loans
has_many :books, :through => :book_loans
end
EDIT 2: O seu log de desenvolvimento diz que você não está realmente bater loan
, você está batendo update
. Algumas coisas que você pode fazer: verifique se você tem loan
como um recurso listado em suas rotas; mesclar a ação loan
no update
ação poderia começar a receber tipo de confuso, então eu não sei se esta é a melhor abordagem. Estou certo de que há mais, mas essas são as primeiras coisas que surgem à mente. Além disso, eu não sei se você pode adicionar :action => 'loan'
a um tag enviar. Eu acho que ele só vai olhar para isso como se fosse uma opção html. Você pode querer mudar a sua form_for
para ler
<% form_for(@book), :url => { :action => 'loan' } do |x| %>
Uma vez que você tenha a certeza de que as rotas estão em ordem. Mas, como eu disse anteriormente, eu tenho certeza que você será jogado um erro naquela ação porque você não definiu um Book.find
para ele.