我有两张桌子。物品和供应商。物品由供应商出售。所以Item belongs_to:vendor和Vendor has_many:items。这很好。
然而,物品并非总是由销售它们的供应商制造,但有时它们是。所以我的Item表中有一个名为“manufacturer_id”的新列。我没有生成一个名为Manufacturer的新模型,它以相同的方式复制供应商,而是尝试使用复杂的has_many和belongs_to来定义制造商。
见这里:
class Item < ActiveRecord::Base
belongs_to :vendor
belongs_to :manufacturer, :class_name => "Vendor", :foreign_key => "manufacturer_id"
end
class Vendor < ActiveRecord::Base
has_many :items
has_many :manufactured_items, :class_name => "Item", :foreign_key => "manufacturer_id"
end
在items表中填充manufacturer_id在Create命令上按预期工作:
Item.create(:manufacturer => Vendor.find_by_abbrev("INV"))
我甚至可以将制造商作为操作
item.manufacturer
返回:
<Vendor:0x007ff06684e398>
但是:
item.manufacturer.name
完全失败了,我得到了错误:
undefined method `name' for nil:NilClass
运行
debug item.manufacturer
给出
--- !ruby/object:Vendor
attributes:
id: 181
name: Invitrogen
website: http://www.invitrogen.com/
created_at: 2012-01-08 01:39:07.486375000Z
updated_at: 2012-01-08 01:39:07.486375000Z
abbrev: INV
所以item.manufacturer.name应该返回上面那个供应商对象的名称,供应商:0x007ff06684e398。
我在这里做错了什么?
此外,一旦我开始工作,我希望能够同样打电话:
vendor.manufactured_items
获取具有该供应商的manufacturer_id的所有项目。有没有直接的方法呢?
我最后的努力可能涉及到:
manufacturer = Vendor.new(item.manufacturer)
但这似乎完全错了,并且违背了rails文档: http://guides.rubyonrails.org/association_basics.html#self-joins
请帮忙!
答案 0 :(得分:3)
好的,我实际上为你和posted it on GitHub构建了一个演示Rails 3.1项目。我在README
文件中包含了控制台输出,以证明item.seller.name
和item.manufacturer.name
之类的通话以及vendor.sold_items.first.manufacturer.name
之类的往返通话可以让您使用例如,获取特定供应商的第一个销售商品的制造商名称。
我认为事情的根源,正如你所指出的那样,vendor
和manufacturer
,无论出于何种意图和目的,都是相同的。出于这个原因,我将它们简单地组合到Vendor
类中,并以这样的方式设置外键关系,使其按照我认为你想要的方式工作。
特别是,您应该注意README文件,该文件具有我运行的控制台会话输出以显示它的工作情况。您还需要查看两个模型类及其关联的定义方式,以及spec/factories.rb
文件如何设置虚假数据库数据(我已将其包含在下面)。 / p>
今天早上重新阅读你的问题时,我不确定你做错了什么,但你可能会把它归结为某个地方你的协会中的一个微妙的错误。这可能是你真的关闭的情况,但并不完全相同。 :d
以下是代码中的一些snipets:
应用/模型/ item.rb的强>
class Item < ActiveRecord::Base
belongs_to :seller, :class_name => "Vendor"
belongs_to :manufacturer, :class_name => "Vendor"
end
应用/模型/ vendor.rb 强>
class Vendor < ActiveRecord::Base
has_many :sold_items, :class_name => "Item", :foreign_key => :seller_id
has_many :manufactured_items, :class_name => "Item", :foreign_key => :manufacturer_id
end
<强>规格/ factories.rb 强>
require 'populator'
require 'faker'
FactoryGirl.define do
factory :vendor do |v|
v.name {Populator.words(1..3)}
v.website {Faker::Internet.domain_name}
v.abbr {(["ABC", "DEF", "GHI", "JKL", "MNO", "PQR", "STU", "VWX", "YZ1"])[rand(9)]}
end
factory :item do |i|
i.association :seller, :factory => :vendor
i.association :manufacturer, :factory => :vendor
i.name {Populator.words(3..5)}
end
end
<强> LIB /任务/ populator.rake 强>
namespace :db do
desc "Erase database"
task :erase => :environment do
puts "Erasing..."
[Vendor, Item].each(&:delete_all)
end
desc "Erase and fill database"
task :populate => [:environment, :erase] do
require 'populator'
require 'faker'
puts "Populating: enjoy this random pattern generator while you wait..."
50.times{Factory.create(:vendor)}
Vendor.all.each do |v|
# This line actually has a bug in it that makes all `seller_id` and `manufacturer_id`
# columns always contain a value in the range 0..50. That means
# `rake db:populate` will only actually work the first time, but
# I think you get the idea of how this should work.
10.times{Factory.create(:item, :seller_id => (rand(50) + 1), :manufacturer_id => (rand(50) + 1))}
print (['\\', '/', '_', '|'])[rand(4)]
end
puts ""
end
end