如何在 Ruby on Rails 中保存多个类之间的关联记录

发布于 2024-08-05 22:12:27 字数 3202 浏览 4 评论 0原文

我创建了三个类来表示 BooksPeopleBookLoans。虽然我能够通过图书贷款展示人与书籍的关联,但我一直在为我的数据库播种。

我现在需要保存一本书的结帐。我的目的是通过图书控制器来执行此操作。具体来说,在 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

不羁少年 2024-08-12 22:12:27

当您has_many一个模型时,您可以访问一些额外的方法,其中两个特别是collection.buildcollection.create - build 就像 newcreate 就像 create :)。查看 有很多文档。在这种情况下,您可以将 add_loan 重写为

def add_loan (person_id)
  book_loans.create(:book_id   => id,
                    :person_id => person_id,
                    :out_date  => Date.current)
end

或类似的内容。

要回答有关视图将向控制器发送什么内容的问题,它将照常发送 params 哈希值,但如果您只想将 person_id 传递给 add_loan,你可以提取它。假设您上面的视图部分包含在图书借阅表单中,您可以通过 params[:book_loan][:person_id] 访问该人员。您还需要首先在该操作中进行查找,否则 @book 将为零。

希望这有帮助。干杯。

编辑:我不确定您现在的方式是否有效,但我认为您想更改您的 Person 模型以读取

class Person < ActiveRecord::Base
  has_many :book_loans
  has_many :books, :through => :book_loans
end

编辑2:您的开发日志表明您实际上并未点击 < code>loan,您正在点击update。您可以做一些事情:检查以确保您的路线中列出了贷款资源;将 loan 操作合并到 update 操作中 - 可能会变得有点混乱,所以我不知道这是否是最好的方法。我确信还有更多,但这些是首先浮现在脑海中的事情。另外,不知道是否可以添加 :action =>; 'loan' 到提交标签。我认为它只会将其视为 html 选项。您可能需要将 form_for 更改为

<% form_for(@book), :url => { :action => 'loan' } do |x| %>

在确保路线有序后读取。但正如我之前所说,我很确定您会在该操作中抛出错误,因为您尚未为其定义 Book.find

When you has_many a model, you get access to a few extra methods, two in particular are collection.build and collection.createbuild is like new, create is like create :). Have a look at the has many documentation. In this case, you could rewrite add_loan as

def add_loan (person_id)
  book_loans.create(:book_id   => id,
                    :person_id => person_id,
                    :out_date  => Date.current)
end

or 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 the person_id to add_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 by params[: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 read

class Person < ActiveRecord::Base
  has_many :book_loans
  has_many :books, :through => :book_loans
end

EDIT 2: Your development log says you aren't actually hitting loan, you're hitting update. A few things you could do: check to make sure you have loan as a listed resource in your routes; merge the loan action into the update 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 your form_for to read

<% form_for(@book), :url => { :action => 'loan' } do |x| %>

once 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文