2010-01-27 22 views
5

que estoy tratando de hacer lo siguiente en un proyecto Ruby on Rails:has_many: a través de una asociación has_and_belongs_to_many

class FoodItem < ActiveRecord::Base 
    has_and_belongs_to_many :food_categories 
    has_many :places, :through => :food_categories 
end 

class FoodCategory < ActiveRecord::Base 
    has_and_belongs_to_many :food_items 
    belongs_to :place 
end 

class Place < ActiveRecord::Base 
    has_many :food_categories 
    has_many :food_items, :through => :food_category 
end 

Pero llamando al método de instancia some_food_item.places me da el siguiente error:

ActiveRecord::StatementInvalid: PGError: ERROR: column 
food_categories.food_item_id does not exist 
LINE 1: ...laces".id = "food_categories".place_id WHERE (("food_cate... 

: SELECT "places".* FROM "places" INNER JOIN "food_categories" ON "places".id = "food_categories".place_id WHERE (("food_categories".food_item_id = 1)) 

Qué tiene perfecto sentido: debido a las HABTM en FoodItem y FoodCategory tengo la tabla de asignación llamada food_categories_food_items.

¿Qué debo hacer para obtener some_food_item.places para buscar correctamente los lugares a través de la tabla de asignación en lugar de buscar food_item_id en la tabla food_categories?

Respuesta

6

Mi primera versión de la respuesta fue incorrecta, pero esta funciona perfectamente. Hice un par de errores tipográficos la primera vez (el peligro de no crear realmente una aplicación para probar) pero esta vez lo verifiqué. Y se necesita un complemento, pero esto es fácil. primero, instale el complemento:

script/plugin install git://github.com/ianwhite/nested_has_many_through.git 

Esto instala la solución alternativa de Ian White, y funciona a la perfección. Ahora, los modelos copiados directamente de la configuración de la prueba aplicación que conseguir este trabajo:

class FoodItem < ActiveRecord::Base 
    has_many :food_category_items 
    has_many :food_categories, :through => :food_category_items 
    has_many :places, :through => :food_categories 
end 

class FoodCategory < ActiveRecord::Base 
    has_many :food_category_items 
    has_many :food_items, :through => :food_category_items 
    belongs_to :place 
end 

class FoodCategoryItem < ActiveRecord::Base 
    belongs_to :food_item 
    belongs_to :food_category 
end 

class Place < ActiveRecord::Base 
    has_many :food_categories 
    has_many :food_category_items, :through => :food_categories 
    has_many :food_items, :through => :food_category_items 
end 

ahora "lejos" asociaciones funcionan igual de bien. place_instance.food_items y food_item.places ambos funcionan sin problemas, así como las asociaciones más simples involucradas. Sólo como referencia, aquí está mi esquema para mostrar donde todas las claves externas van:

create_table "food_categories", :force => true do |t| 
    t.string "name" 
    t.integer "place_id" 
    t.datetime "created_at" 
    t.datetime "updated_at" 
end 

create_table "food_category_items", :force => true do |t| 
    t.string "name" 
    t.integer "food_item_id" 
    t.integer "food_category_id" 
    t.datetime "created_at" 
    t.datetime "updated_at" 
end 

create_table "food_items", :force => true do |t| 
    t.string "name" 
    t.datetime "created_at" 
    t.datetime "updated_at" 
end 

create_table "places", :force => true do |t| 
    t.string "name" 
    t.datetime "created_at" 
    t.datetime "updated_at" 
end 

Espero que esto ayude!

ACTUALIZACIÓN: Esta pregunta ha aparecido varias veces recientemente. Escribí un artículo, nesting your has_many :through relationships, para explicar en detalle. Incluso tiene una aplicación de ejemplo acompañante en GitHub para descargar y jugar.

+0

¡Ciertamente ayuda! Gracias a ambos por la solución y también por el aviso de HABTM. ¡Estoy seguro de que este no será el único lugar donde aplicaré esta solución! –

2

Hace unos meses escribí an article about this. En resumen, has_many a través de una asociación has_and_belongs_to_many no está permitido por Rails. Sin embargo, se puede simular en parte la relación haciendo algo como esto:

class FoodItem < ActiveRecord::Base 
    has_and_belongs_to_many :food_categories 
    named_scope :in_place, lambda{ |place| 
    { 
     :joins  => :food_categories, 
     :conditions => {:food_categories => {:id => place.food_category_ids}}, 
     :select  => "DISTINCT `food_items`.*" # kill duplicates 
    } 
    } 
end 

class FoodCategory < ActiveRecord::Base 
    has_and_belongs_to_many :food_items 
    belongs_to :place 
end 

class Place 
    has_many :food_categories 
    def food_items 
    FoodItem.in_place(self) 
    end 
end 

esto le dará el método some_food_item.places que buscan.

-1

Esto es correcto, porque no se puede aplicar "tiene muchos pasos" en una tabla de unión. En esencia, estás tratando de extender la relación un grado más allá de lo que realmente puedes. HABTM (has_and_belongs_to_many) no es una solución muy robusta para la mayoría de los problemas.

En su caso, recomendaría agregar un modelo llamado FoodCategoryItem y cambiar el nombre de su tabla de combinación para que coincida. También necesitarás volver a agregar el campo de la clave principal. A continuación, configurar sus asociaciones modelo así:

class FoodItem < ActiveRecord::Base 
    has_many :food_categories, :through => :food_category_items 
    has_many :places, :through => :food_categories 
end 

class FoodCategory < ActiveRecord::Base 
    has_many :food_items, :through => :food_category_items 
    belongs_to :place 
end 

class FoodCategoryItems < ActiveRecord::Base 
    belongs_to :food_item 
    belongs_to :food_category 
end 

class Place < ActiveRecord::Base 
    has_many :food_categories 
    has_many :food_items, :through => :food_categories 
end 

nota, también ha corregido un error tipográfico en "Lugar -> has_many: food_items". Esto debería ocuparse de lo que necesita y darle la ventaja adicional de poder agregar funcionalidad a su modelo de "unión" de FoodCategoryItems en el futuro.

+0

No puedo hacer que esto funcione, cuando llamo a some_food_item.places me sale el mismo error. Aparentemente es una limitación en ActiveRecord (ver http://www.ruby-forum.com/topic/115029). –

+0

Tienes razón, lo siento. Agregué una respuesta que arregla todo. –

1

Estoy usando Rails 3.2.13 y el Rasmus, su configuración original ahora parece funcionar bien en una HABTM.

Sugiero que los usuarios prueben primero antes de intentar una solución alternativa.

Cuestiones relacionadas