6

Hola a todos, tengo algo de un requisito interesante para mi proyecto. Necesito una relación has_one donde sea una clase o la otra, pero sin herencia. Podría salirme con la herencia si fuera la única forma, pero los dos registros asociados tienen datos completamente diferentes y no están relacionados en absoluto.RoR: has_one "or the other"? (O, polimorfismo sin la herencia.)

Lo que necesito averiguar es algo como lo siguiente.

# 1. Foo never belongs to anything. 
# 2. Foo MUST have one assigned sub-record for validity. 
# 3. Foo can only have either Bar or Baz assigned. 
# 4. Bar and Baz have only ONE common property, and aren't 
# related in either data or implementation. 

class Foo < ActiveRecord::Base 
    # Attributes: id, name, value 
    has_one :assignment, :foreign_key => 'assigned_to', :readonly => true 
      # Could really use an :object_type for has_one here... 
end 

class Bar < ActiveRecord::Base 
    # Attributes: name,... 
end 

class Baz < ActiveRecord::Base 
    # Attributes: name,... 
end 

Dónde Foo tiene una asignación, de tipo ya sea Bar o Baz; solo comparten una columna común, así que quizás pueda hacer un objeto principal a partir de eso. Sin embargo, si los hago heredar de un objeto común (cuando los datos que contienen realmente son naranjas y manzanas) ¿debo hacer una tabla para el registro? ¿Puedo quizás salirse con la suya si el registro es un registro abstracto, pero los niños no?

Supongo que ahora puede ver mi dificultad. Soy bastante nuevo en RoR pero me encanta hasta ahora. Estoy seguro de que hay una forma de evitar esto, pero seré condenado si no puedo descubrir qué es.

Respuesta

8

Está tratando de modelar algo que no se ajusta al paradigma de la base de datos relacional. Todas las referencias en SQL tienen un origen y un objetivo.

FWIW, Asociaciones polimórficas también es un antipatrón porque rompe esta regla. Debería ser una pista de que se trata de un diseño roto cuando la documentación dice que debe renunciar a una restricción de integridad referencial para que funcione.

Necesita que Foo tenga dos relaciones has_one: una para Bar y otra para Baz. Luego, implemente una lógica de clase para intentar asegurar que solo se rellene una referencia en cualquier instancia de Foo. Es decir, de las referencias a Bar y Baz, uno debe tener un valor y el otro debe ser nulo, pero esto es algo que su código debe verificar y aplicar.

+0

Buen punto, sin embargo, a veces no se nos concede plena libertad sobre cómo se diseñan las bases de datos o cómo se relacionan los datos. Especialmente cuando se trabaja con un producto o biblioteca producida por otra persona. Las dos relaciones has_one funcionarán bien; gracias por tu visión. –

0

Quizás una forma de hacer esto sea crear asociaciones de uno para Foo, para Bar y Baz. Luego crea un método llamado asignación y asignación = que puede ser la única forma de acceder a Bar y Baz. Puede verificar cuál de los dos has_ones no es nulo en el método get y devolver ese. En el método de asignación, puede -verificar cuál es el tipo de variable pasada y establecer la relación correcta de tener-uno con ese objeto y establecer el otro en cero. Eso debería cubrir todas tus bases sin ser demasiado complicado.

Cuestiones relacionadas