Ale*_*bio 9 ruby-on-rails ruby-on-rails-4
我有这个型号
class Book < ActiveRecord::Base
has_many :accountings
has_many :commodities, through: :accountings
end
class Accounting < ActiveRecord::Base
belongs_to :book
belongs_to :commodity
end
class Commodity < ActiveRecord::Base
has_many :accountings
end
Run Code Online (Sandbox Code Playgroud)
我的书形式是:
<%= form_for(book) do |f| %>
<%= render 'shared/error_messages', object: book %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :commodity_ids %>
<%= f.collection_select :commodity_ids, Commodity.all, :id, :name, {}, {multiple: true} %>
<br />
<%= f.submit class: 'btn btn-primary' %>
<% end %>
Run Code Online (Sandbox Code Playgroud)
在BooksController:
def new
@book = Book.new
end
def create
@book = Book.new(book_params)
if @book.save
redirect_to @book
else
render 'new'
end
end
def edit
@book = Book.find(params[:id])
end
def update
@book = Book.find(params[:id])
if @book.update_attributes(book_params)
redirect_to @book
else
render 'edit'
end
end
private
def book_params
params.require(:book).permit(:name, commodity_ids: [])
end
Run Code Online (Sandbox Code Playgroud)
这一切都很好.并且Accounting在commodity_ids更新时添加和删除记录.(1)
现在我需要添加一个新模型:Company由于Book并且Commodity由所有公司共享,Accounting必须属于company,并且不在所有系统中共享.并Accounting成为:
class Accounting < ActiveRecord::Base
belongs_to :book
belongs_to :commodity
belongs_to :company
end
Run Code Online (Sandbox Code Playgroud)
该Company模型:
class Company < ActiveRecord::Base
has_many :accountings
end
Run Code Online (Sandbox Code Playgroud)
Accounting 它不仅仅是商品和书籍(和公司)之间的关系,它也代表了一种商业模式.
这里的约束是:当a Commodity添加到a时Book,Accounting必须为每个 创建一个new Company.(2)
我确实尝试Book过companies,通过accountings.但是,它不起作用.即使它不代表商业模式,书也不关心公司,我认为Book是链接模型的好人选.(3)
现在我正在考虑添加一个新模型BookCommodity,通过这个模型将书籍和商品联系起来,并且在保存时,这个新模型会生成Accounting所有人需要的记录companies.(4)
在添加第五个模型之前,我想问你是否有更好的方法来管理这些东西?
编辑
在Github,你可以找到一个demo_finance项目,只有这篇文章的代码.它有4个分支:
这里的关键任务是在书中添加或删除商品,并更新帐户,就像它在版本(1)(主分支)中工作一样.
编辑#2
我试着这样做:
class Book < ActiveRecord::Base
has_many :accountings
has_many :commodity_companies, through: :accountings
has_many :commodities, through: :commodity_companies
end
Run Code Online (Sandbox Code Playgroud)
但它不起作用.在更新它提出:
ActiveRecord::HasManyThroughNestedAssociationsAreReadonly
Cannot modify association 'Book#commodities' because it goes through more than one other association.
Run Code Online (Sandbox Code Playgroud)
我也尝试这样做:
class Book < ActiveRecord::Base
has_many :accountings do
def build(attributes = {}, &block)
Company.all.each do |company|
@association.build(attributes.merge(company_id: company.id), &block)
end
end
end
has_many :commodities, through: :accountings
end
Run Code Online (Sandbox Code Playgroud)
但是,这个构建操作不会在更新书上调用commodity_ids=.
回答我自己的问题:
我还没有选择这个答案作为正确的答案。因为,尽管它有效,但我认为可能有更好的答案,值得赏金。
我让它覆盖了该方法commodity_ids=,还需要添加dependent: :destroy会计选项和-> {uniq}商品范围。
class Book < ActiveRecord::Base
has_many :accountings, dependent: :destroy
has_many :commodities, ->{ uniq }, through: :accountings
def commodity_ids=(ids)
self.accountings = Commodity.where(id: ids).map do |commodity|
Company.all.map do |company|
accountings.find_by(company_id: company.id, commodity_id: commodity.id) ||
accountings.build(company: company, commodity: commodity)
end
end.flatten
end
def commodities=(records)
self.commodity_ids = records.map(&:id)
end
end
class Commodity < ActiveRecord::Base
end
class Company < ActiveRecord::Base
end
class Accounting < ActiveRecord::Base
belongs_to :book
belongs_to :commodity
belongs_to :company
def to_s
"#{book.name} - #{company.name} - #{commodity.name}"
end
end
Run Code Online (Sandbox Code Playgroud)
从头开始在控制台运行:
~/ (main) > Book.create name: 'Book 1'
=> #<Book id: 1, name: "Book 1", created_at: "2015-09-30 11:47:23", updated_at: "2015-09-30 11:47:23">
~/ (main) > Commodity.create name: 'Commodity 1'
=> #<Commodity id: 1, name: "Commodity 1", created_at: "2015-09-30 11:47:37", updated_at: "2015-09-30 11:47:37">
~/ (main) > Commodity.create name: 'Commodity 2'
=> #<Commodity id: 2, name: "Commodity 2", created_at: "2015-09-30 11:47:40", updated_at: "2015-09-30 11:47:40">
~/ (main) > Commodity.create name: 'Commodity 3'
=> #<Commodity id: 3, name: "Commodity 3", created_at: "2015-09-30 11:47:42", updated_at: "2015-09-30 11:47:42">
~/ (main) > Company.create name: 'Company 1'
=> #<Company id: 1, name: "Company 1", created_at: "2015-09-30 11:47:51", updated_at: "2015-09-30 11:47:51">
~/ (main) > Company.create name: 'Company 2'
=> #<Company id: 2, name: "Company 2", created_at: "2015-09-30 11:47:54", updated_at: "2015-09-30 11:47:54">
~/ (main) > bb = Book.first
=> #<Book id: 1, name: "Book 1", created_at: "2015-09-30 11:47:23", updated_at: "2015-09-30 11:47:23">
~/ (main) > bb.commodity_ids = ['', nil, 1, '3']
=> [
[0] "",
[1] nil,
[2] 1,
[3] "3"
]
~/ (main) > bb.save
=> true
~/ (main) > bb.reload
=> #<Book id: 1, name: "Book 1", created_at: "2015-09-30 11:47:23", updated_at: "2015-09-30 11:47:23">
~/ (main) > bb.accountings.map(&:to_s)
=> [
[0] "Book 1 - Company 1 - Commodity 1",
[1] "Book 1 - Company 2 - Commodity 1",
[2] "Book 1 - Company 1 - Commodity 3",
[3] "Book 1 - Company 2 - Commodity 3"
]
~/ (main) > bb.commodities.map(&:name)
=> [
[0] "Commodity 1",
[1] "Commodity 3"
]
~/ (main) > bb.commodity_ids = ['']
=> [
[0] ""
]
~/ (main) > bb.save
=> true
~/ (main) > bb.reload
=> #<Book id: 1, name: "Book 1", created_at: "2015-09-30 11:47:23", updated_at: "2015-09-30 11:47:23">
~/ (main) > bb.commodities.map(&:name)
=> []
~/ (main) > bb.accountings.map(&:to_s)
=> []
~/ (main) >
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
518 次 |
| 最近记录: |