繁体   English   中英

Rails:类继承和复杂的多态has_many:通过关联

[英]Rails: Class inheritance and complex polymorphic has_many :through association

我正在开发的应用程序具有3个主要模型和许多单表继承模型:

  1. 用户
    1. 专业的
    2. 代表人
  2. 分类
    1. 类别
    2. 话题
    3. 职业
    4. 地区性
    5. 区域
    6. 国家

有多种用户( UserProfessional < UserRepresentant < User ),它们都从User类继承并具有单个表继承。

有多种分类法( Category < TaxonomyTopic < TaxonomyProfession < Taxonomy Topic < TaxonomyProfession < Taxonomy Locality < TaxonomyRegion < TaxonomyCountry < Taxonomy ),它们均从具有单一表继承的分类法类继承。

问题和专业人员也通过多对多关系进行分类(他们可以具有很多主题,很多专业,很多类别,等等。)

现在,我正在寻找一种在这些多态对象之间建立多对多关系的方法。 我已经尝试过has_many :through解决方案,并创建了一个Classification类。

迁移文件:

class CreateClassifications < ActiveRecord::Migration
  def change
    create_table :classifications, :id => false do |t|
      t.references :classifiable, :null => false, :default => 0, :polymorphic => true
      t.references :taxonomy,     :null => false, :default => 0, :polymorphic => true
    end

    add_index :classifications, [:classifiable_id, :taxonomy_id]
    add_index :classifications, [:taxonomy_id, :classifiable_id]
  end
end

模型文件:

class Classification < ActiveRecord::Base

  attr_accessible :classifiable, :classifiable_id, :classifiable_type,
                  :taxonomy, :taxonomy_id, :taxonomy_type

  belongs_to :classifiable, :polymorphic => true
  belongs_to :taxonomy,     :polymorphic => true

end

然后,我has_many :through问题,专业人员和分类法的关联添加了has_many :through

分类学

has_many :classifications, :as => :taxonomy, :foreign_key => :taxonomy_id
has_many :classifiables, :through => :classifications, :source => :classifiable
has_many :users,         :through => :classifications, :source => :classifiable, :source_type => "User"
has_many :professionals, :through => :classifications, :source => :classifiable, :source_type => "Professional"
has_many :representants, :through => :classifications, :source => :classifiable, :source_type => "Representant"
has_many :questions,     :through => :classifications, :source => :classifiable, :source_type => "Question"
has_many :guides,        :through => :classifications, :source => :classifiable, :source_type => "Guide"

Question.rb

has_many :classifications, :as => :classifiable, :foreign_key => :classifiable_id, :dependent => :destroy
has_many :taxonomies, :through => :classifications, :source => :taxonomy
has_many :topics,     :through => :classifications, :source => :taxonomy, :source_type => "Topic"

专业版

has_many :classifications, :as => :classifiable, :foreign_key => :classifiable_id, :dependent => :destroy
has_many :taxonomies,  :through => :classifications, :source => :taxonomy
has_many :topics,      :through => :classifications, :source => :taxonomy, :source_type => "Topic"
has_many :professions, :through => :classifications, :source => :taxonomy, :source_type => "Profession"

现在,设置好所有这些之后,事情就无法很好地进行了...

  1. 我似乎无法为专业人士或问题分配分类法(即Question.create(:title => "Lorem Ipsum Dolor Sit Amet", :author => current_user, :topics => [list of topics,...])除未保存的主题外,效果很好。)

  2. Where子句无法正常工作(例如Question.joins(:topics).where(:conditions => {:topics => {:id => [list of topics,...]}})失败,并出现一个no such column: "Topics"."id"错误。

有什么帮助吗? 谢谢!

更新

我已经按照指示安装了gem'store_base_sti_class'。 它对分类模型产生了预期的效果。

#<Classification classifiable_id: 1, classifiable_type: "Professional", taxonomy_id: 17, taxonomy_type: "Topic">

但是,当我查询主题( Professional.find(1).topics )时,ActiveRecord仍在寻找类“ User”而不是“ Professional” ...

SELECT "taxonomies".* FROM "taxonomies" INNER JOIN "classifications" ON "taxonomies"."id" = "classifications"."taxonomy_id" WHERE "taxonomies"."type" IN ('Topic') AND "classifications"."classifiable_id" = 1 AND "classifications"."classifiable_type" = 'User' AND "classifications"."taxonomy_type" = 'Topic'

知道如何针对两者进行修复吗?

对于问题2,where子句中的键应映射到表名,而不是关联名。 所以我想你会想要:

Question.joins(:topics).where(Topic.table_name => {:id => [...]})

对于问题1,似乎当您设置question.topics = [...] ,Rails创建的分类对象的分类类型设置为“ Taxonomy”(而不是“ Topic”)。 这似乎是由于Rails的through_association.rb:51所致,它采用了要存储的模型的base_class ,而不仅仅是实际的类名。

我能够通过分类模型上的before_validation回调解决此问题。 在我看来,替代方法是对实际的Rails关联代码进行修补,以使此行为可配置。

class Classification < ActiveRecord::Base
  attr_accessible :classifiable, :classifiable_id, :classifiable_type,
                  :taxonomy, :taxonomy_id, :taxonomy_type

  belongs_to :classifiable, polymorphic: true
  belongs_to :taxonomy, polymorphic: true
  before_validation :set_valid_types_on_polymorphic_associations

  protected

  def set_valid_types_on_polymorphic_associations
    self.classifiable_type = classifiable.class.model_name if classifiable
    self.taxonomy_type = taxonomy.class.model_name if taxonomy
  end
end

更新

在设置关联范围时,似乎还有另一个Rails决策(在preloader / association.rb:113中 )使用model.base_class.sti_name而不是model.sti_name

那个宝石应该为你照顾好这个。 有关如何包装has_many :as选项的信息,请参见store_base_sti_class_for_3_1_and_above.rb:135 在我的本地环境中,这可以按预期工作:

$ bundle exec rails console
irb(main):001:0> topics = 3.times.map { Topic.create }
irb(main):002:0> p = Professional.new
irb(main):003:0> p.topics = topics
irb(main):004:0> p.save!
irb(main):005:0> exit

$ bundle exec rails console
irb(main):001:0> puts Professional.find(1).topics.to_sql
SELECT "taxonomies".* FROM "taxonomies" INNER JOIN "classifications" ON "taxonomies"."id" = "classifications"."taxonomy_id" WHERE "taxonomies"."type" IN ('Topic') AND "classifications"."classifiable_id" = 2 AND "classifications"."classifiable_type" = 'Professional' AND "classifications"."taxonomy_type" IN ('Topic')
irb(main):002:0> Professional.find(1).topics.count
=> 3

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM