Как использовать полиморфные ассоциации в моделях с пространством имен?

Я понимаю, как вы можете использовать параметр class_name в активном каталоге для ссылки на модели пространств имен:

has_one :slide, :class_name => '::Refinery::Slides::Slide'

И как использовать полиморфные ассоциации

has_one :slide, :as => :slideable

Можете ли вы использовать их вместе?

has_one :slide, :class_name => '::Refinery::Slides::Slide', :as => :slideable

И если да, то как вы определяете полиморфную ассоциацию?

belongs_to :slideable, :polymorphic => true, class_name='::Refinery::Slideables::Slideable' #NO   

Я работаю с RefineryCMS, и каждый добавленный вами движок получает пространство имен в Refinery :: PluralModel :: SingularModel. По сути, я хочу иметь возможность связать слайд либо с ситуацией, либо с работой. Вот актуальные модели.

module Refinery
  module CaseStudies
    class CaseStudy < Refinery::Core::BaseModel
      attr_accessible :title, :description, :position
      has_one :slide, :class_name => '::Refinery::Slides::Slide', :as => :slideable
    end
  end
end

module Refinery
  module Works
    class Work < Refinery::Core::BaseModel
      attr_accessible :title, :description, :position, 
      has_one :slide, :class_name => '::Refinery::Slides::Slide', :as => :slideable
    end
  end
end

module Refinery
  module Slides
    class Slide < Refinery::Core::BaseModel
      attr_accessible :slide_id, :caption, :position, :slideable_id, :slideable
      belongs_to :slide, :class_name => '::Refinery::Image'
      belongs_to :slideable, :polymorphic => true
    end
  end
end

Похоже, я могу сказать slide.slideable.title, но получаю сообщение об ошибке: undefined method `title 'для nil: NilClass

apidoc указывает, что inverse_of не может использоваться с полиморфизмом, но ничего не говорит о class_name


person rakitin    schedule 24.04.2013    source источник


Ответы (1)


Чтобы полиморфная ассоциация работала, вам нужно добавить столбец типа вместе со столбцом идентификатора в модели, которая принадлежит другой модели. Столбец типа будет хранить имя класса, поэтому нет необходимости указывать в общей ассоциации.

Например, Comment может принадлежать Question или Answer. Назовем вопросы и ответы «комментируемыми». В комментарии должны быть столбцы для :commentable_id и :commentable_type.

class Comment < ActiveRecord::Base
  # has columns :commentable_id and :commentable_type
  belongs_to :commentable, :polymorphic => true
end

class Question < ActiveRecord::Base
  has_many :comments, :as => :commentable
end

class Answer < ActiveRecord::Base
  has_many :comments, :as => :commentable
end

# example
comment = Comment.new(:body => "Nice answer!")
comment.commentable = Answer.find(1)
comment.save

Итак, в вашем случае вы можете удалить параметры :class_name из Work и CaseStudy и убедиться, что вы добавили столбцы :slide_type (для использования с :slide_id) и :slideable_type (для использования с :slideable_id) в свой Slide. Все должно «просто работать» с именами классов в пространстве имен.

module Refinery
  module CaseStudies
    class CaseStudy < Refinery::Core::BaseModel
      attr_accessible :title, :description, :position
      has_one :slide, :as => :slideable
    end
  end
end

module Refinery
  module Works
    class Work < Refinery::Core::BaseModel
      attr_accessible :title, :description, :position, 
      has_one :slide, :as => :slideable
    end
  end
end

module Refinery
  module Slides
    class Slide < Refinery::Core::BaseModel
      attr_accessible :slide_id, :caption, :position, :slideable_id, :slideable
      # add :slide_type and :slideable_type columns
      belongs_to :slide, :polymorphic => true
      belongs_to :slideable, :polymorphic => true
    end
  end
end
person rossta    schedule 24.04.2013
comment
спасибо за такой обстоятельный ответ! Я скептически отношусь к возможности удалить class_name, но вот оно - вернусь и дам вам знать. - person rakitin; 24.04.2013
comment
похоже, вы правы, я могу удалить class_name, потому что в моей форме я устанавливаю slideable_type, как этот slide.radio_button (: slideable_type, Refinery :: Works :: Work,: checked = ›(: slideable_type == 'работает ')) - person rakitin; 24.04.2013