2010-01-27 12 views
5

Io sto cercando di fare quanto segue in un Ruby on Rails progetto:has_many: attraverso un'associazione 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 

Ma chiamando il metodo di istanza some_food_item.places mi dà il seguente errore:

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)) 

Quale ha perfettamente senso - a causa degli HABTM su FoodItem e FoodCategory ho la tabella di mappatura denominata food_categories_food_items.

Cosa devo fare per ottenere some_food_item.places in modo che punti correttamente nella tabella di mapping anziché cercare un food_item_id nella tabella food_categories?

risposta

6

La mia prima versione della risposta era errata, ma questa funziona perfettamente. Ho fatto un paio di errori di battitura la prima volta (il rischio di non creare effettivamente un'app da testare) ma questa volta ho verificato. E un plugin è necessario, ma questo è facile. per prima cosa, installa il plug-in:

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

Questo installa la soluzione di Ian White e funziona perfettamente. Ora i modelli, copiate direttamente dalla configurazione di prova app che per ottenere questo lavoro:

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 

Ora "lontano" associazioni funzionano altrettanto bene. place_instance.food_items e food_item.places entrambi funzionano perfettamente, così come le associazioni più semplici coinvolte. Solo per riferimento, ecco il mio schema per mostrare dove vanno tutte le chiavi esterne:

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 

Spero che questo aiuti!

UPDATE: Questa domanda è comparsa poche volte di recente. Ho scritto un articolo, nesting your has_many :through relationships, per spiegare in dettaglio. Ha anche un'applicazione di esempio di accompagnamento su GitHub per scaricare e giocare con.

+0

Aiuta sicuramente! Grazie a entrambi per la soluzione e anche per l'heads up su HABTM. Sono sicuro che questo non sarà l'unico posto in cui applicherò questa soluzione! –

2

Alcuni mesi fa ho scritto an article about this. In breve, l'associazione has_many tramite l'associazione has_and_belongs_to_many non è consentita da Rails. Tuttavia, è possibile in parte simulare il rapporto facendo qualcosa di simile:

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 

Questo vi darà il metodo some_food_item.places che cercate.

-1

Questo è corretto, perché non è possibile eseguire il comando "has many through" su una tabella di join. In sostanza, stai cercando di estendere la relazione di un grado più di quanto tu possa realmente. HABTM (has_and_belongs_to_many) non è una soluzione molto efficace per la maggior parte dei problemi.

Nel tuo caso, ti consiglio di aggiungere un modello chiamato FoodCategoryItem e di rinominare la tabella di join in modo che corrisponda. Dovrai inoltre aggiungere nuovamente il campo chiave primaria. Poi di impostazione vostre associazioni modello come questo:

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, ho anche fissato un errore di battitura nel "Luogo -> has_many: food_items". Questo dovrebbe occuparsi di ciò di cui hai bisogno e ti darà il vantaggio di essere in grado di aggiungere funzionalità al tuo modello "join" di FoodCategoryItems in futuro.

+0

Non riesco a farlo funzionare - quando si chiama some_food_item.places ottengo lo stesso errore. Apparentemente è una limitazione in ActiveRecord (vedi http://www.ruby-forum.com/topic/115029). –

+0

Hai ragione, mi dispiace. Ho aggiunto una risposta che risolve tutto. –

1

Utilizzo Rails 3.2.13 e i The Rasmus, il tuo setup originale ora sembra funzionare bene su un HABTM.

Suggerirei agli utenti di provare prima prima di tentare una soluzione alternativa.

Problemi correlati