I posted something about this a week ago wihtout response, but have made some progress since. However, I''m still not getting exactly what I want. OK, three core tables: create_table :reference_items do |t| t.column :title, :string, :limit => 255 t.column :year, :integer, :limit => 4 t.column :type, :string t.column :container_id, :integer t.column :collection_id, :integer t.column :original_id, :integer t.column :event_id, :integer end create_table :contributors do |t| t.column :type, :string, :default => "Person" t.column :sort_name, :string, :limit => 255, :null => false end create_table :contributions do |t| t.column :reference_item_id, :integer t.column :agent_id, :integer t.column :type, :string t.column :position, :integer end Drawing on this article ... <http://blog.hasmanythrough.com/articles/2006/02/28/association-goodness> ... I have no problem using the through relation to be able to do stuff like: some_refitem.contributors.first.sort_name Where I get stuck is that I need to be able to subclass contributions so that I can do stuff like: some_refitem.authors some_refitem.translators So I need to be able to essentially treat each subclass as a list. Can STI + through associations currently handle this, and is yes, how? If not, any suggestions on how to achieve what I''m after? Bruce
Bruce D''Arcus wrote:> Where I get stuck is that I need to be able to subclass contributions so > that I can do stuff like: > > some_refitem.authors > some_refitem.translators > > So I need to be able to essentially treat each subclass as a list. > > Can STI + through associations currently handle this, and is yes, how? > > If not, any suggestions on how to achieve what I''m after?Glad my blog was useful for you. I think the entry on working with polymorphic :through associations should be useful as well. http://blog.hasmanythrough.com/articles/2006/04/03/polymorphic-through Now that I think of it, you can adapt that technique to work with concrete classes in STI too. Just define an association with a condition specifying the concrete class in the type field. Something like: has_many :contributors, :through => :contributions has_many :authors, : through => :contributions, :condition => "type = ''Author''" has_many : translators, : through => :contributions, :condition => "type = ''Translator''" I notice you have a ''type'' in contributions and contributors both. You really need both of those? I''m not sure which type you want to make the different associations based on. You may need to change my example around depending on which. (Including your model class code in the question would be helpful.) Also, it looks like contributions has a position for acting as a list. If you really want each concrete class to act as a different list, you might try defining the acts_as_list using a :scope that includes the ''type'' of the record. See the docs on acts_as_list for creating scopes as conditions. -- Josh Susser http://blog.hasmanythrough.com -- Posted via http://www.ruby-forum.com/.
Josh Susser <josh@...> writes: [... snip ...]> Now that I think of it, you can adapt that technique to work with > concrete classes in STI too. Just define an association with a condition > specifying the concrete class in the type field. Something like: > > has_many :contributors, :through => :contributions > has_many :authors, : through => :contributions, :condition => "type = > ''Author''" > has_many : translators, : through => :contributions, :condition => > "type = ''Translator''"Thanks. I was missing the :condition; will give it a go.> I notice you have a ''type'' in contributions and contributors both. You > really need both of those? I''m not sure which type you want to make the > different associations based on. You may need to change my example > around depending on which. (Including your model class code in the > question would be helpful.)The way I have it now (not including any associations) is: Contributor < ActiveRecord::Base end Person < Contributor end Organization < Contributor end [aside: it would be nice if AR would allow me to store types in separate tables by default] But a given contributor can play different roles in different circumstances, so they are not subclasses of Contributor. For example, I can be a translator and an author, and an editor. So I have then have that captured in the Contribution class: Contribution < ActiveRecord::Base end Authorship < Contribution end Editorship < Contribution end Translation < Contribution end So the idea is that translators, authors, editors, publishers, etc. (all of which I need to handle) are each different roles that associate a contributor to an item. I''m not sure that''s the best way to do that (am new to this), but it seems like it is to me (?).> Also, it looks like contributions has a position for acting as a list.Correct. I need to treat each subclass as a separate list.> If you really want each concrete class to act as a different list, you > might try defining the acts_as_list using a :scope that includes the > ''type'' of the record.OK, thanks! Bruce
Josh Susser <josh@...> writes:> Now that I think of it, you can adapt that technique to work with > concrete classes in STI too. Just define an association with a condition > specifying the concrete class in the type field. Something like: > > has_many :contributors, :through => :contributions > has_many :authors, : through => :contributions, :condition => "type = > ''Author''" > has_many : translators, : through => :contributions, :condition => > "type = ''Translator''" >Not quite working. Schema: create_table "contributions", :force => true do |t| t.column "reference_item_id", :integer t.column "contributor_id", :integer t.column "type", :string t.column "position", :integer end create_table "contributors", :force => true do |t| t.column "name", :string t.column "type", :string, :default => "Person" end create_table "reference_items", :force => true do |t| t.column "title", :string t.column "type", :string end Models: class Contributor < ActiveRecord::Base has_many :contributions, :dependent => true has_many :reference_items, :through => :contributions end class Contribution < ActiveRecord::Base belongs_to :reference_item belongs_to :contributor end class Author < Contribution acts_as_list :scope => "type = ''Author''" end class ReferenceItem < ActiveRecord::Base has_many :contributors, :through => :contributions has_many :authors, :through => :contributions, :condition => "type = ''Author''" end Book and Person are just straight subclasses of ReferenceItem and Contributor respectively. So now on the console:>> book = Book.new(:title => "Book Title")=> #<Book:0x247d2d0 @attributes={"title"=>"Book Title", "type"=>"Book"}, @new_record=true>>> book.save=> true>> jd = Person.new(:name => "Jane Doe")=> #<Person:0x25f85c4 @attributes={"name"=>"Jane Doe", "type"=>"Person"}, @new_record=true>>> jd.save=> true>> author = Author.new(:reference_item_id => 1, :contributor_id => 1)=> #<Author:0x25e40ec @attributes={"reference_item_id"=>1, "type"=>"Author", "position"=>nil, "contributor_id"=>1}, @new_record=true>>> author.save=> true>> x = Book.find(1)=> #<Book:0x25be888 @attributes={"title"=>"Book Title", "type"=>"Book", "id"=>"1"}>>> x.authorsNoMethodError: undefined method `authors'' for #<Book:0x25be888>>> x.contributorsActiveRecord::HasManyThroughAssociationNotFoundError: ActiveRecord::HasManyThroughAssociationNotFoundError I get the same error if I am using the main ReferenceItem class. Am I missing something obvious? Bruce
Bruce D''Arcus <bdarcus.lists@...> writes:> Not quite working.For benefit of the archives, the solution is ...> class ReferenceItem < ActiveRecord::Base > has_many :contributors, > :through => :contributions > has_many :authors, > :through => :contributions, > :condition => "type = ''Author''" > endTo replace the "condition" with ":source => :contributor". This now works correctly: x.authors.first.name Bruce