Posts Tagged ‘migrate’

Herencia en Rails

Friday, June 20th, 2008

Después de leer y reunir información de varios sitios pude entender como implementar la herencia en Ruby on Rails; utiliza el enfoque STI.

STI: En este enfoque cada modelo/entidad perteneciente a la jerarquía de la herencia se representa en una única tabla. Está tiene un atributo especial (columna) que identifica a qué modelo/entidad corresponde dicho registro/tupla.

Por lo tanto, tendremos una única tabla con todos los atributos de todos los modelos/entidades más un atributo especial.

Tuve problemas precisamente cuando tengo una herencia con más de un nivel, la solución es igual que tener un nivel de herencia. Dejo aquí un ejemplo ilustrativo del asunto.

Herencia citada en el ejemplo

Cada modelo podríamos asignarle los siguientes atributos

  • Vehiculo
    • nombre, del tipo String
  • Aereo
    • alas, del tipo Integer
  • Terrestre
    • ruedas, del tipo Integer
  • Auto
    • puertas, del tipo integer

Ahora creemos las cuatro clases en app/models las cuales quedarán con el siguiente aspecto

  • vehiculo.rb
    
    class Vehiculo < ActiveRecord::Base
    end
    
  • aereo.rb
    
    class Aereo < Vehiculo
    end
    
  • terrestre.rb
    
    class Terrestre < Vehiculo
    end
    
  • auto.rb
    
    class Auto < Terrestre
    end
    
    

Necesitamos crear un archivo migrate para mapear nuestros modelos a la base de datos (en este caso MySQL), este archivo tendría el siguiente aspecto:


class Ejemplo < ActiveRecord::Migration
  def self.up
    create_table :vehiculos do |t|
      t.column :nombre, :string
      t.column :alas, :integer
      t.column :ruedas, :integer
      t.column :puertas, :integer
      t.column :type, :string
    end
  end

  def self.down
    drop_table :vehiculos
  end
end

Indicaremos a Rails que implemente el esquema anterior, sólo bastará un rake:migrate. Esto nos genera la siguiente tabla



mysql> describe vehiculos;
+———+————–+——+—–+———+—————-+
| Field   | Type         | Null | Key | Default | Extra          |
+———+————–+——+—–+———+—————-+
| id      | int(11)      | NO   | PRI | NULL    | auto_increment |
| nombre  | varchar(255) | YES  |     | NULL    |                |
| alas    | int(11)      | YES  |     | NULL    |                |
| ruedas  | int(11)      | YES  |     | NULL    |                |
| puertas | int(11)      | YES  |     | NULL    |                |
| type    | varchar(255) | YES  |     | NULL    |                |
+———+————–+——+—–+———+—————-+
6 rows in set (0.01 sec)

Por último probemos como Rails resuelve la herencia, para esto utilizaremos la consola (script/console). Ire creando objetos (que se mapearan como registros en la base de datos) de los distintos modelos/entidades.


>> a = Vehiculo.create
=> #<Terrestre id: 1, nombre: nil, alas: nil, ruedas: nil, puertas: nil, type: "Vehiculo">

>> b = Aereo.create
=> #<Terrestre id: 2, nombre: nil, alas: nil, ruedas: nil, puertas: nil, type: "Aereo">
>> c = Terrestre.create
=> #<Terrestre id: 3, nombre: nil, alas: nil, ruedas: nil, puertas: nil, type: "Terrestre">
>> d = Auto.create
=> #<Auto id: 4, nombre: nil, alas: nil, ruedas: nil, puertas: nil, type: "Auto">

Como se puede ver, según el tipo de modelo creado, Rails establece el valor de la columna type. Ahora haré una serie de find, así se podrá ver cómo soluciona la herencia Rails.


>> Vehiculo.find :all
=> [#><Vehiculo id: 1, nombre: nil, alas: nil, ruedas: nil, puertas: nil, type: nil>, #<Aereo id: 2, nombre: nil, alas: nil, ruedas: nil, puertas: nil, type: "Aereo">, #<Terrestre id: 3, nombre: nil, alas: nil, ruedas: nil, puertas: nil, type: "Terrestre">, #<Auto id: 4, nombre: nil, alas: nil, ruedas: nil, puertas: nil, type: "Auto">]

Acá indiqué que me traiga todos los Vehiculos, es lo que se esperaba, me trajo todo los registros, ya que cada modelo hereda de Vehiculo y por la herencia cada modelo es un vehiculo.


>> Aereo.find :all
=> [#<Aereo id: 2, nombre: nil, alas: nil, ruedas: nil, puertas: nil, type: "Aereo">]

Como cree un sólo registro del tipo Aereo, me trae ese único. Ahora veamos el problema que comenté al inicio del post


>> Terrestre.find :all
=> [#<Terrestre id: 3, nombre: nil, alas: nil, ruedas: nil, puertas: nil, type: "Terrestre">, #<Auto id: 4, nombre: nil, alas: nil, ruedas: nil, puertas: nil, type: "Auto">]

Bien, me ha traído los dos registros, acá es igual que en el primer find que mostré, ya que Auto es un Terrestre (por la herencia), por lo tanto Rails debe traerme también ese tipo de registros


>> Auto.find :all
=> [#<Auto id: 4, nombre: nil, alas: nil, ruedas: nil, puertas: nil, type: "Auto">]

De este modo podemos implementar, en Ruby on Rails, el concepto de herencia..