如何在 Ruby on Rails 中保存多个类之间的关联记录
我创建了三个类来表示 Books、People 和 BookLoans。虽然我能够通过图书贷款展示人与书籍的关联,但我一直在为我的数据库播种。
我现在需要保存一本书的结帐。我的目的是通过图书控制器来执行此操作。具体来说,在 BooksController 中创建借阅操作。虽然这在理论上对我来说是有意义的,但我在实现适当的语法时遇到了困难。
我添加了从书籍的展示视图借阅书籍的功能。该视图现在包含一个表单,该表单使用图书控制器的贷款操作来记录贷款。
我已将我认为合适的方法添加到我的 Book 模型中。在 theIV 的帮助下,我在控制器中捕获了适当的信息。不幸的是,当我在图书显示视图上按“贷款”时,没有记录 book_loan 记录。
我缺少什么?
图书模型
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
BooksController 的借阅方法
def loan
@book.add_loan(params[:book_loan][:person_id])
redirect_to :action => 'book', :id => params[:id]
end
带借阅表格的图书展示视图
<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 %>
图书借阅模型
class BookLoan < ActiveRecord::Base
belongs_to :book
belongs_to :person
end
人员模型
class Person < ActiveRecord::Base
has_many :book_loans
has_many :books, :through => :book_loans
end
开发日志
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
I've created three classes to represent Books, People, and BookLoans. While I am able to show the association of People to Books through BookLoans I've been seeding my database.
I now need to save a checkout of a book. It was my intention to do this action through the book controller. Specifically, creating a loan action in the BooksController. While this makes sense to me in theory I am having a terrible time implementing the appropriate syntax.
I've added the ability to loan a book from the show view of a book. This view now contains a form which uses the loan action of the book controller to record the loan.
I've added what I believe are the appropriate methods to my Book model. With the help of theIV I have captured the appropriate information in the Controller. Unfortunately, when I press Loan on the book show view a book_loan record is no being recorded.
What am I missing?
Book Model
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
Loan Method from BooksController
def loan
@book.add_loan(params[:book_loan][:person_id])
redirect_to :action => 'book', :id => params[:id]
end
Book Show View w/ Loan Form
<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 Model
class BookLoan < ActiveRecord::Base
belongs_to :book
belongs_to :person
end
Person Model
class Person < ActiveRecord::Base
has_many :book_loans
has_many :books, :through => :book_loans
end
Development Log
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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
当您
has_many
一个模型时,您可以访问一些额外的方法,其中两个特别是collection.build
和collection.create
-build
就像new
,create
就像create
:)。查看 有很多文档。在这种情况下,您可以将 add_loan 重写为或类似的内容。
要回答有关视图将向控制器发送什么内容的问题,它将照常发送
params
哈希值,但如果您只想将person_id
传递给add_loan
,你可以提取它。假设您上面的视图部分包含在图书借阅表单中,您可以通过params[:book_loan][:person_id]
访问该人员。您还需要首先在该操作中进行查找,否则@book
将为零。希望这有帮助。干杯。
编辑:我不确定您现在的方式是否有效,但我认为您想更改您的
Person
模型以读取编辑2:您的开发日志表明您实际上并未点击 < code>loan,您正在点击
update
。您可以做一些事情:检查以确保您的路线中列出了贷款
资源;将loan
操作合并到update
操作中 - 可能会变得有点混乱,所以我不知道这是否是最好的方法。我确信还有更多,但这些是首先浮现在脑海中的事情。另外,不知道是否可以添加:action =>; 'loan'
到提交标签。我认为它只会将其视为 html 选项。您可能需要将form_for
更改为在确保路线有序后读取。但正如我之前所说,我很确定您会在该操作中抛出错误,因为您尚未为其定义
Book.find
。When you
has_many
a model, you get access to a few extra methods, two in particular arecollection.build
andcollection.create
—build
is likenew
,create
is likecreate
:). Have a look at the has many documentation. In this case, you could rewrite add_loan asor something similar.
To answer your question about what the view will be sending to the controller, it will send the
params
hash as usual, but if you just want to pass theperson_id
toadd_loan
, you can extract that. Assuming that the part of the view you have above is wrapped in a form for a book loan, you can access the person byparams[:book_loan][:person_id]
. You'll also want to do a find in that action first, or else@book
is going to be nil.Hope this helps. Cheers.
EDIT: I'm not sure if the way you have it right now works, but I think you want to change your
Person
model to readEDIT 2: Your development log says you aren't actually hitting
loan
, you're hittingupdate
. A few things you could do: check to make sure you haveloan
as a listed resource in your routes; merge theloan
action into theupdate
action—could start getting kind of messy so I don't know if this is the best approach. I'm sure there are more, but those are the first things that pop to mind. Also, I don't know if you can add:action => 'loan'
to a submit tag. I think it will just look at that as if it were an html option. You might want to change yourform_for
to readonce you've made sure that the routes are in order. But as I said earlier, I'm pretty sure you will be thrown an error on that action because you haven't defined a
Book.find
for it.