ruby on rails - references - Rieles ha_one vs pertenece_a semántica
rails references (2)
Tengo un modelo que representa un elemento de Content
que contiene algunas imágenes. El número de imágenes se fija, ya que estas referencias de imágenes son muy específicas del contenido. Por ejemplo, el modelo de Content
refiere al modelo de Image
dos veces (imagen de perfil e imagen de fondo). Estoy tratando de evitar un has_many
genérico y has_one
a varios has_one
''s. La estructura actual de la base de datos se ve así:
contents
- id:integer
- integer:profile_image_id
- integer:background_image_id
images
- integer:id
- string:filename
- integer:content_id
Simplemente no puedo averiguar cómo configurar las asociaciones correctamente aquí. El modelo de Content
podría contener dos referencias de belongs_to
a una Image
, pero eso no parece semánticamente correcto, idealmente, una imagen pertenece al contenido, o en otras palabras, el contenido tiene dos imágenes.
Esto es lo mejor que pude pensar (rompiendo la semántica):
class Content
belongs_to :profile_image, :class_name => ''Image'', :foreign_key => ''profile_image_id''
belongs_to :background_image, :class_name => ''Image'', :foreign_key => ''background_image_id''
end
¿Estoy lejos, y hay una mejor manera de lograr esta asociación?
Basado en la guía de asociaciones AR , creo que deberías usar has_one
. No tiene sentido que una imagen posea un Contenido ... el Contenido seguramente posee la imagen. De la guía:
La distinción se encuentra en el lugar donde coloca la clave externa (va en la tabla para la clase que declara la asociación con el que pertenece), pero también debe reflexionar sobre el significado real de los datos. La relación has_one dice que uno de algo es tuyo, es decir, que algo apunta hacia ti.
Por último, no estoy seguro de que necesites contenidos e imágenes para tener claves externas. Mientras las imágenes hagan referencia al content_id, creo que estás bien.
La respuesta simple es configurar sus asociaciones a la inversa de lo que tiene, así:
# app/models/content.rb
class Content < ActiveRecord::Base
has_one :profile_image, :class_name => ''Image''
has_one :background_image, :class_name => ''Image''
end
# app/models/image.rb
class Image < ActiveRecord::Base
belongs_to :content
end
No necesita las claves externas ''background_image_id'' y ''profile_image_id'' en la tabla de contenido en absoluto.
Sin embargo, hay una solución más elegante: la herencia de una sola tabla. Configúrelo ahora en caso de que desee que las imágenes de fondo y de perfil se comporten de manera ligeramente diferente en el futuro, además de que aclarará su código hoy.
Primero, agregue una columna a su tabla de imágenes llamada tipo:
# command line
script/generate migration AddTypeToImages type:string
rake db:migrate
Ahora configura tus modelos de esta manera:
# app/models/content.rb
class Content < ActiveRecord::Base
has_one :profile_image
has_one :background_image
end
# app/models/image.rb
class Image < ActiveRecord::Base
belongs_to :content
end
# app/models/background_image.rb
class BackgroundImage < Image
# background image specific code here
end
# app/models/profile_image.rb
class ProfileImage < Image
# profile image specific code here
end
Ahora puedes hacer todo tipo de cosas como obtener una lista de todas las imágenes de fondo:
# script/console
BackgroundImage.all
Esto es más fiel al modelo de datos que está intentando crear, permite la expansión más fácil en el futuro y le ofrece algunos métodos nuevos e interesantes hoy.
ACTUALIZAR:
Desde entonces, he creado un artículo de blog llamado Herencia de una sola tabla con pruebas que incluye más detalles y cubre las pruebas.