Forum: Ruby on Rails STI and type tables?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Bruce D'Arcus (Guest)
on 2006-04-22 18:05
(Received via mailing list)
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
Sean H. (Guest)
on 2006-04-22 20:46
(Received via mailing list)
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
Bruce D'Arcus (Guest)
on 2006-04-22 22:19
(Received via mailing list)
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
Ashley M. (Guest)
on 2006-04-22 23:21
(Received via mailing list)
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
Bruce D'Arcus (Guest)
on 2006-04-23 17:21
(Received via mailing list)
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
Kevin O. (Guest)
on 2006-04-23 17:40
(Received via mailing list)
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
>removed_email_address@domain.invalid
>http://lists.rubyonrails.org/mailman/listinfo/rails


_Kevin
Ashley M. (Guest)
on 2006-04-23 21:06
(Received via mailing list)
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
This topic is locked and can not be replied to.