# Many to many

**URL:** <https://www.ruby-forum.com/t/many-to-many/184076>\
**Category:** IT\
**Created:** [February 24, 2010, 6:28pm UTC](https://www.ruby-forum.com/t/many-to-many/184076 "2010-02-24T18:28:06Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![Renato\_DaprSSSS](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/bc8723/32.png) [@Renato\_DaprSSSS](https://www.ruby-forum.com/u/Renato_DaprSSSS)\
**Post date:** [February 24, 2010, 6:28pm UTC](https://www.ruby-forum.com/t/many-to-many/184076/1 "2010-02-24T18:28:06Z")

</div>

Ciao a tutti,  
mi sono appena iscritto alla ML e già eccomi qui con una richiesta di  
aiuto.

Avrei bisogno di capire come funziona il collegamento fra due tabelle  
“many to many” con Ruby on Rails.

In particolare ho la necessità di collegare una tabella names ad una  
tabella investigations attarverso la tab join  
invetigations\_names.

Io ho proceduto mttendo nei models names e investigations:  
has\_and\_belongs\_to\_many :investigations  
has\_and\_belongs\_to\_many :names

e in new names:  
\<%= f.label :investigations\_id %\>  
  
\<%= f.collection\_select :id, Investigation.find(:all, :order =\>  
“indagine ASC”), :id, :indagine, :include\_blank=\>true%\>  
\<%= link\_to ‘New’ , new\_investigation\_path %\>

Quando provo ad inserire da new names una indagine questa non viene  
inserita nella tabella join e  
Mongrell mi dice :

WARNING: Can’t mass-assign these protected attributes: id

riferendosi al fatto che viene passato l’id di investigations.

Grazie, Renato.

---

<div class="post-metadata">

**Author:** ![Renato\_DaprSSSS](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/bc8723/32.png) [@Renato\_DaprSSSS](https://www.ruby-forum.com/u/Renato_DaprSSSS)\
**Post date:** [February 24, 2010, 7:07pm UTC](https://www.ruby-forum.com/t/many-to-many/184076/2 "2010-02-24T19:07:19Z")

</div>

2010/2/24 Renato Daprà [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid):

> Ciao a tutti,  
> mi sono appena iscritto alla ML e già eccomi qui con una richiesta di aiuto.

Ciao e benvenuto!

> \<%= f.label :investigations\_id %\>  
>   
> \<%= f.collection\_select :id, Investigation.find(:all, :order =\> “indagine ASC”), :id, :indagine, :include\_blank=\>true%\>  
> \<%= link\_to ‘New’ , new\_investigation\_path %\>
> 
> Quando provo ad inserire da new names una indagine questa non viene inserita nella tabella join e  
> Mongrell mi dice :
> 
> WARNING: Can’t mass-assign these protected attributes: id
> 
> riferendosi al fatto che viene passato l’id di investigations.

Certo, perché la select si chiama id, non investigation\_id 🙂

P.S. fatti un favore, ed evita di inserire chiamate al db dentro le  
view.

Nel controller (metodo new o edit, a seconda) metti:  
@investications = Investigation.all, :order =\> “indagine ASC”

e nella view:

\<%= f.collection\_select :investigation\_id, @investigations, :id,  
:indagine, :include\_blank=\>true %\>

Te ne verrà una vita più felice e meno problemi di salute!

pietro

---

<div class="post-metadata">

**Author:** ![Renato\_DaprSSSS](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/bc8723/32.png) [@Renato\_DaprSSSS](https://www.ruby-forum.com/u/Renato_DaprSSSS)\
**Post date:** [February 24, 2010, 10:17pm UTC](https://www.ruby-forum.com/t/many-to-many/184076/3 "2010-02-24T22:17:30Z")

</div>

Il giorno 24/feb/2010, alle ore 19.06, Pietro G. ha scritto:

> 2010/2/24 Renato Daprà [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid):
> 
> > Ciao a tutti,  
> > mi sono appena iscritto alla ML e già eccomi qui con una richiesta di aiuto.
> 
> Ciao e benvenuto!  
> Grazie
> 
> > e in new names:
> 
> Certo, perché la select si chiama id, non investigation\_id 🙂

Ho provato ma non funziona comunque.  
Credo sia causato dal parametro di investigation che viene passato come  
. “id”=\>“1”  
e quindi il messaggio di errore é :  
WARNING: Can’t mass-assign these protected attributes: id

> Te ne verrà una vita più felice e meno problemi di salute!  
> Grazie del consiglio 🙂

> pietro
> 
> * * *

Renato

---

<div class="post-metadata">

**Author:** ![Renato\_DaprSSSS](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/bc8723/32.png) [@Renato\_DaprSSSS](https://www.ruby-forum.com/u/Renato_DaprSSSS)\
**Post date:** [February 24, 2010, 7:32pm UTC](https://www.ruby-forum.com/t/many-to-many/184076/4 "2010-02-24T19:32:32Z")

</div>

Ciao Renato e benvenuto!

Non ho capito perchÃ¨ utilizzi la collection select, non ti conviene  
utilizzare una selezione multipla come i check box? Per esempio:

\<% @investigations.each do |item| %\>  
\<%= check\_box\_tag “investigation\_ids[]”, item.id %\>  
\<%= f.label item.indagine%\>  
\<% end %\>

e poi nel controller fai qualcosa tipo:

if @names.save  
@names.investigations \<\<  
Investigation.find(params[:investigation\_ids])  
ecc…

Non so se Ã¨ il modo piÃ¹ efficiente ed elegante per realizzarlo

---

<div class="post-metadata">

**Author:** ![Renato\_DaprSSSS](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/bc8723/32.png) [@Renato\_DaprSSSS](https://www.ruby-forum.com/u/Renato_DaprSSSS)\
**Post date:** [February 24, 2010, 10:21pm UTC](https://www.ruby-forum.com/t/many-to-many/184076/5 "2010-02-24T22:21:35Z")

</div>

Il giorno 24/feb/2010, alle ore 19.32, Marco M. ha scritto:

> Ciao Renato e benvenuto!  
> Grazie
> 
> Non ho capito perchè utilizzi la collection select, non ti conviene  
> utilizzare una selezione multipla come i check box? Per esempio:  
> Perché le indagini possono essere 10 \> e quindi le check box credo scomode  
> ecc…  
> Potresti definire meglio la parte del controller
> 
> Non so se è il modo più efficiente ed elegante per realizzarlo

Potrei comunque provare.

Grazie, Renato

---

<div class="post-metadata">

**Author:** ![Renato\_DaprSSSS](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/bc8723/32.png) [@Renato\_DaprSSSS](https://www.ruby-forum.com/u/Renato_DaprSSSS)\
**Post date:** [February 25, 2010, 12:16pm UTC](https://www.ruby-forum.com/t/many-to-many/184076/6 "2010-02-25T12:16:53Z")

</div>

Renato DaprÃ wrote:

La collection select, crea una select con selezione singola, dovresti  
provare ad inserire l’opzione multiple tra le opzioni html, se ho tempo  
faccio qualche prova.

Se le checkbox le disponi ordinatamente su piÃ¹ colonne, credo che fino  
una ventina siano utilizzabili, il mio caso era un’interfaccia di  
amministrazione quindi avevo meno esigenze estetiche.

L’esempio di prima era la view per selezione multipla e non veniva  
impostato il valore di ogni item (nel tuo caso, indagine) in quanto  
forzato dalla nuova selezione, non era l’esempio che volevo mostrarti.  
Nella view normale controlli se il singolo item Ã¨ presente tra la  
collection associata a name:

#VIEW  
\<% @investigations.each do |item| %\>  
\<%= check\_box\_tag  
“investigation\_ids[]”,  
item.id,  
@name.investigations.include?(item) %\>  
\<%= f.label item.indagine%\>  
\<% end %\>

La create Ã¨ molto semplice:

#CONTROLLER

#CREATE  
if @name.save  
if params[:investigation\_ids]  
@name.investigations \<\<  
Investigation.find(params[:investigation\_ids])  
end  
else  
#ecc.  
end

l’update un pÃ² piÃ¹ complessa ma ho usato delle variabili di transizione  
in modo che puoi verificare col debug.  
Un’altra soluzione piÃ¹ semplice come codice ma piÃ¹ onerosa lato db  
sarebbe quella di fare a priori una delete\_all ed aggiungere quello che  
si Ã¨ selezionato, io preferisco questa:

#UPDATE  
if @name.update\_attributes(params[:name])  
if params[:investigations\_ids]  
now = @name.investigations.map(&:id)  
selected = params[:investigation\_ids].map{|item| item.to\_i}  
to\_delete = now - selected  
to\_add = selected - now  
@name.investigations.delete(Investigation.find(to\_delete)) if  
to\_delete.any?  
@name.investigations \<\< Investigation.find(to\_add) if to\_add.any?  
else  
@name.investigations.delete\_all  
end  
end

---

<div class="post-metadata">

**Author:** ![Renato\_DaprSSSS](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/bc8723/32.png) [@Renato\_DaprSSSS](https://www.ruby-forum.com/u/Renato_DaprSSSS)\
**Post date:** [February 25, 2010, 3:49pm UTC](https://www.ruby-forum.com/t/many-to-many/184076/7 "2010-02-25T15:49:21Z")

</div>

Ho generalizzato anche la routine per la view inserendola in un helper,  
puoi anche scegliere il numero di colonne per impaginare le check box.

#VIEW  
\<%= manage\_to\_many\_checkbox(@investigations, @name, “investigation”, 5)  
%\>

#HELPER  
def manage\_to\_many\_checkbox(collection, object, str\_object\_many, cols=1)  
html = “”  
i=0  
collection.each do |item|  
html \<\< “

” if i % cols == 0  
id\_html = “#{str\_object\_many}\_#{item.id.to\_s}”  
html \<\< “” \<\<  
check\_box\_tag("#{str\_object\_many}\_ids[]",  
item.id,  
object.send("#{str\_object\_many}s").include?(item),  
{:id =\> id\_html}) \<\<  
label\_tag(id\_html, item.name) \<\<  
“”  
i+=1  
html \<\< “
” if i % cols == 0  
end  
html \<\< “”  
end

---

<div class="post-metadata">

**Author:** ![Renato\_DaprSSSS](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/bc8723/32.png) [@Renato\_DaprSSSS](https://www.ruby-forum.com/u/Renato_DaprSSSS)\
**Post date:** [February 25, 2010, 2:09pm UTC](https://www.ruby-forum.com/t/many-to-many/184076/8 "2010-02-25T14:09:00Z")

</div>

Anzi, mi sono spinto oltre, ho creato una routine generica da piazzare  
sotto application controller o sotto lib:

#controllers\application\_controller.rb  
def manage\_to\_many(object, str\_object\_many)  
model = eval str\_object\_many.capitalize  
str\_objects\_many = “#{str\_object\_many}s”  
now = object.send(str\_objects\_many).map(&:id)  
if params["#{str\_object\_many}\_ids"]  
selected = params["#{str\_object\_many}\_ids"].map{|item| item.to\_i}  
to\_delete = now - selected  
to\_add = selected - now  
object.send(str\_objects\_many).delete(model.find(to\_delete)) if  
to\_delete.any?  
object.send(str\_objects\_many) \<\< model.find(to\_add) if to\_add.any?  
else  
object.send(str\_objects\_many).delete\_all if now.any?  
end  
end

Ã¨ generica, va bene per qualsiasi oggetto e sia per la create che per  
l’update, nel tuo caso la richiameresti cosÃ¬:

#CREATE  
if @name.save  
manage\_to\_many(@name, “investigation”)  
else  
#ecc.  
end

#UPDATE  
if @name.update\_attributes(params[:name])  
manage\_to\_many(@name, “investigation”)  
else  
#ecc.  
end

Nel caso di altri modelli basta cambiare i parametri passati, ad esempio  
ruoli ed autorizzazioni:  
manage\_to\_many(@role, “authorization”)
