Rails 3.1 объединяет расширенную модель Has_many и own_to - PullRequest
3 голосов
/ 08 января 2012

У меня есть две таблицы.Предметы и продавцы.Товары продаются продавцами.Таким образом Item Item_to: vendor и Vendor has_many: items.Это прекрасно работает.

Однако, Предметы не всегда производятся Продавцами, которые продают их, но иногда это так.Таким образом, у меня есть новый столбец в моей таблице Item под названием "factory_id".Вместо того, чтобы сгенерировать новую модель с именем Manufacturer, которая идентично дублирует Vendor, я попытался создать комплекс has_many и assign_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

Заполнение factory_id в таблице элементовработает должным образом в командах создания:

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

такname.manufacturer.name должно возвращать имя для этого объекта вендора, указанного выше, Vendor: 0x007ff06684e398.

Что я здесь не так делаю?

Кроме того, как только я получу эту работу, я бы хотелбыть в состоянии аналогичным образом вызвать:

vendor.manufactured_items

, чтобы получить все элементы, которые имеют идентификатор_производителя этого поставщика.Есть ли простой способ сделать это тоже?

Мое последнее усилие в канаве может включать необходимость:

factory = Vendor.new (item.manufacturer)

Но этокажется совершенно неправильным и идет вразрез с документацией по рельсам: http://guides.rubyonrails.org/association_basics.html#self-joins

пожалуйста, помогите!

1 Ответ

3 голосов
/ 08 января 2012

Хорошо, я на самом деле создал для вас демонстрационный проект Rails 3.1 и разместил его на GitHub . Я включил вывод консоли в файл README, чтобы доказать, что такие вызовы, как item.seller.name и item.manufacturer.name, работают, а также вызовы туда и обратно, такие как vendor.sold_items.first.manufacturer.name, которые позволяют вам узнать имя производителя например, первый проданный товар для конкретного поставщика.

Я думаю, корень всего, как вы заметили, в том, что vendor и manufacturer для всех намерений и целей идентичны. По этой причине я просто объединил их в класс Vendor и настроил отношения с внешним ключом таким образом, чтобы они работали так, как я думаю, вы хотите.

В частности, вам следует обратить внимание на файл README , в котором есть вывод сеанса консоли, который я запустил, чтобы показать, как он работает. Вы также захотите взглянуть на два класса моделей и на то, как определяются их ассоциации, а также на файл spec/factories.rb о том, как он устанавливает ложные данные базы данных (я включил их ниже).

Перечитывая ваш вопрос сегодня утром, я не уверен, что вы делали неправильно, но вы, вероятно, можете объяснить это тонкой ошибкой в ​​ваших ассоциациях где-то. Это, вероятно, тот случай, когда вы действительно близко, но не совсем там. : D

Вот некоторые фрагменты кода:

приложение / модели / 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

Библиотека / задачи / 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...