STI and type tables?

So by default to get STI, I would have something like:

create_table :items do |t|
  t.column "type", :string
  t.column "title", :string
end

But if I want to store those type values in a separate table, such
that I have …

create_table :items do |t|
  t.column "item_type_id", :string
  t.column "title", :string
end

create_table :item_types do |t|
  t.column "name", :string
end

How would I do that?

Bruce

I don’t know that you do. I never thought about doing that, but I’m
not sure why you’d want to. What’s your reasoning?

I mainly have model files for each type and keep track of them that
way. I guess that since I know what types/models there’ll be (and
there won’t be many), I’m comfortable hardcoding that type of stuff.
But maybe there’s a better way?

Sean

Sean H. <seanhussey@…> writes:

I don’t know that you do. I never thought about doing that, but I’m
not sure why you’d want to. What’s your reasoning?

I mainly have model files for each type and keep track of them that
way. I guess that since I know what types/models there’ll be (and
there won’t be many), I’m comfortable hardcoding that type of stuff.

Two things:

First, I’m really not much of RDBMS person, but it seems to me that the
very
logic of these systems is to normalize. If I have critical typing
information
(as I do here), then it seems more intuitive to me that I ought to store
it in
its own table, rather than as a simple string.

Second, this is largely a prototype for a non-Rails app. I want
programmers to
be able to pick up the SQL schema and use it without having to dig into
Rails-specific code to understand what’s going on.

Bruce

On Apr 22, 2006, at 7:17 pm, Bruce D’Arcus wrote:

Second, this is largely a prototype for a non-Rails app. I want
programmers to
be able to pick up the SQL schema and use it without having to dig
into
Rails-specific code to understand what’s going on.

Bruce

Bruce

How about creating your tables like this

 create_table :items do |t|
   t.column "type", :string
   t.column "title", :string
 end

 create_table :item_types do |t|
   t.column "type_code", :string
 end

 add_index :item_types, :type_code, :unique

 execute "ALTER TABLE items ADD CONSTRAINT FK__items__item_types
              FOREIGN KEY (type) REFERENCES item_types(type_code);"

Now Rails is happy, and your database has referential integrity.
Admittedly, you are using a meaningful string to look up a
meaningless integer (item_types.id) which is back-to-front, but it’s
better than nothing.

There is a better way of solving this, but you said you aren’t an
RDBMS person, so maybe the above is a suitable compromise between
design and complexity?

Ashley

Ashley M. <work@…> writes:

[…]

Now Rails is happy, and your database has referential integrity.
Admittedly, you are using a meaningful string to look up a
meaningless integer (item_types.id) which is back-to-front, but it’s
better than nothing.

Thanks Ashley.

So does this suggest that it’s quite hard to make Rails “happy” with
subclassing based on a value in another table?

Bruce

The STI mechanism is set up this way. For one thing, usually your type
column just holds the name of a class. This way rails can just create a
new object of the correct type from the returned value. Otherwise, you
might need to do another query to look up the relevant class type.

If you really want to, you could dig into the STI code and set it up to
join the type information from another table, but that seems like more
work than it’s worth.

On Sunday, April 23, 2006, at 1:20 PM, Bruce D’Arcus wrote:

So does this suggest that it’s quite hard to make Rails “happy” with
subclassing based on a value in another table?

Bruce


Rails mailing list
[email protected]
http://lists.rubyonrails.org/mailman/listinfo/rails

_Kevin

On Apr 23, 2006, at 2:20 pm, Bruce D’Arcus wrote:

So does this suggest that it’s quite hard to make Rails “happy” with
subclassing based on a value in another table?

I think so. I’ve had a look through the source and it is based on
extracting a string value from a single column (which defaults to
“type”), in fact this is the code (base.rb):

class ActiveRecord::Base
class << self
private
def instantiate(record)
object =
if subclass_name = record[inheritance_column]
if subclass_name.empty?
allocate
else
require_association_class(subclass_name)
begin
compute_type(subclass_name).allocate
rescue NameError
raise SubclassNotFound, “ERROR MESSAGE REMOVED”
end
end
else
allocate
end
object.instance_variable_set("@attributes", record)
object
end
end
end

If you wanted to make this look up values in another table you could
extend this along these lines:

class ActiveRecord::Base
class << self
def inheritance_table
nil
end

   def set_inheritance_table(value = nil, &block)
     define_attr_method :inheritance_table, value, &block
   end
   alias :inheritance_table= :set_inheritance_table

   private
     def instantiate(record)
       if inheritance_table
         inheritance_table_class = class_name

(inheritance_table).constantize
if subclass_id = record[inheritance_column]
# would need to make this configurable:
subclass_name = inheritance_table_class.find
(subclass_id).type_description
end
else
subclass_name = record[inheritance_column]
end

       object =
         if subclass_name
           if subclass_name.empty?
             allocate
           else
             require_association_class(subclass_name)
             begin
               compute_type(subclass_name).allocate
             rescue NameError
               raise SubclassNotFound, "ERROR MESSAGE REMOVED"
             end
           end
         else
           allocate
         end

       object.instance_variable_set("@attributes", record)
       object
     end
 end

end

The above is mainly untested copy-and-paste, and falls way short of
the standards of Rails code, but I’m sure you get the idea. Maybe
you could write it as a plugin? It’s really easy to rewrite parts of
the Rails core with a plugin, and the above could form the basis of
it. (Also just reading the Rails code has many benefits in itself.)

It depends how much work you want to put into getting the type column
into its own table! I like your idea though. I think STI is messy.
It’ll never win my heart but at least getting the class name into its
own table is an improvement, from a database design point of view.

Ashley