【问题标题】:has_many :through usage, simple, beginner questionhas_many :通过使用,简单,初学者问题
【发布时间】:2011-09-24 10:06:05
【问题描述】:

我正在编写一个简单的会计系统来管理成本。结构是这样的:

Invoice  - can have many products
  Product - can have many costs, also can act_as_tree 
      LineItem  - 
    Product
        LineItem
        LineItem
      Product
      LineItem
  Product
      LineItem
  LineItem

我将这设置为三个类的 has_many 和 belongs_to 但认为以下更合适(基于阅读 Rails 3 Way - 任何缺点都是我缺乏理解;只是试图给出上下文)

Class Invoice < ActiveRecord::Base
    has_many :products
    has_many :line_items, :through => :products
end

 Class Product < ActiveRecord::Base
    belongs_to :invoice
    belongs_to :line_item
 end

class LineItem < ActiveRecord::Base
    has_many :products
    has_many :invoices, :through => :invoices
end

但我不认为这是正常工作。

如果我执行以下操作:

>@i=Invoice.find(1)
>@i.products # this works
>@i.line_items # doesn't work, unidentified method line_items

这是我第一次使用 has_many :through。这是否为我的数据模型正确设置?另外,是否可以将它与acts_as_tree 结合使用-我想说:

>@i.line_items 

并取回该特定发票的所有行项目。可能吗?

感谢帮助

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-3 activerecord


    【解决方案1】:

    首先问一个问题:ProductLineItem 之间有什么关系:有 1 个产品有多个订单项,还是 1 个产品在许多产品中引用了同一个订单项?这个答案的其余部分是基于每个产品都应该有多个订单项的假设。

    我认为你的模型应该这样定义:

    # No change
    Class Invoice < ActiveRecord::Base
      has_many :products
      has_many :line_items, :through => :products
    end
    
    # Changed the relation to :line_items
    Class Product < ActiveRecord::Base
      belongs_to :invoice
      has_many :line_items
    end
    
    class LineItem < ActiveRecord::Base
      belongs_to :products
      # The following then does not make sense
      ##has_many :invoices, :through => :invoices
    end
    

    【讨论】:

    • 你的权利,它是第一个 - 应该在原始问题中添加。但是......这似乎有效!我将不得不使用acts_as_tree 来确保它运行良好。我将如何一举加载所有内容,例如包括所有相关项目? >@i=Invoice.find(1, :include => [:products, :line_items]) 似乎可以做到
    • 不知道,听起来像是另一个问题:-)。您真正的问题是什么:阅读每张发票的产品和订单项?还是阅读整个产品树?
    • 同意提出另一个问题。谢谢回答。想做一些事情,比如查询每个发票的所有 line_items,还有相关的位置等......谢谢回答
    【解决方案2】:

    您为什么选择这种结构?在这种情况下,我通常会这样做

    Class Invoice < ActiveRecord::Base
      has_many :line_items
      has_many :products, :through => :line_items
    end
    
    Class Product < ActiveRecord::Base
      has_many :line_items
      # if you need
      has_many :products, :through => :line_items
    end
    
    class LineItem < ActiveRecord::Base
      belongs_to :products
      belongs_to :invoice
    end
    

    这满足以下要求:

    Invoice 和 Product 具有 Many2Many 关系 Invoice 和 Product 之间的关系是 LineItem,它提供更多信息,如价格、金额、适用的税金等。 您可以通过添加产品来创建 LineItem:

    invoice = Invoice.create
    invoice.products << Product.find_by_name('awesome')
    

    【讨论】:

    • 它有点独特的情况。每个产品都是独一无二的(想象一个二手车批次),所以我们不会处理 100 种完全相同类型的产品。始终是一种独特类型的一种产品。但是,有一些共同的元素可能会发生变化。而且,还使用acts_as_tree 来处理本质上是产品总和的产品。呃……有点丑。让我再考虑一下是否应该改变建模。谢谢
    【解决方案3】:

    您的发票应该只有行项目!这些可以是树形结构,但您不应该直接从您的发票中引用产品(如果产品价格发生变化:这会影响现有的发票!)

    所以,修复你的结构:

    invoice
      line_item   # references a product, but has its own fields (price!)
      line_item
        line_item
    

    每个line_item 应使用belong_to 引用一个产品。这是发票和产品之间的连接模型:

    class Invoice
      has_many :line_items
    end
    
    class LineItem
      belongs_to :invoice
      belongs_to :product
      acts_as_tree # (implies has_many :line_items with the parent invoice_id, etc.)
    end
    
    class Product
      has_many :line_items
    end
    

    这基本上是您创建发票所需的全部内容。您可以将树结构视为独立于Invoice:LineItem:Product 关系。它可以用作平面列表,也可以增强为树。

    如果您的产品通常包含其他产品,并且您需要知道在添加父产品(作为订单项)时要将哪些子产品添加到发票中,那么您也需要树产品:

    class Product
      has_many :line_items
      acts_as_tree
    end
    

    此树形结构独立于订单项中的树形结构。请记住:产品可能会发生变化,这不会影响发票中的现有订单项。

    【讨论】:

    • 这是一个有点独特的域(我更改了名称以尝试使它们更有意义)。每个产品都是一个独特的实例(例如管理项目成本的二手设备)。我也更改了名称,可能没有使用最佳选择。谢谢
    • 好的。不过我希望我说明了一点……重要的是要区分产品本身(目录项)和售出时它在发票上的显示方式(销售项)。
    • 是的,谢谢安德鲁。这是一个有点麻烦的应用程序,我可能通过错误地使用常见的会计术语使它变得更麻烦(可能应该是“佣金对账”而不是发票。对于我写得不好,我深表歉意,感谢您对细节的关注。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-03
    • 1970-01-01
    • 2020-01-04
    相关资源
    最近更新 更多