Has_and_belongs_to_many come riempire la join table?

Siccome nella join table non ho bisogno di gestire nessuna informazione
aggiuntiva alle due foreign keys che mettono in relazione molti a molti
due
tabelle ho preferito usare has_and_belongs_to_many piuttosto che
has_many
:through.
La domanda e’: la tabella di join va popolata manualmente oppure esiste
un
meccanismo automatico che si occupa di popolarla quando vengono inseriti
dati nelle due tabelle in relazione?

Se crei correttamente i due has_and_belongs_to_many la tabella di join
si
autopopola :smiley:

2009/1/27 Mauro [email protected]

Il 27 gennaio 2009 21.35, Mauro [email protected] ha scritto:

Siccome nella join table non ho bisogno di gestire nessuna informazione
aggiuntiva alle due foreign keys che mettono in relazione molti a molti due
tabelle ho preferito usare has_and_belongs_to_many piuttosto che has_many
:through.
La domanda e’: la tabella di join va popolata manualmente oppure esiste un
meccanismo automatico che si occupa di popolarla quando vengono inseriti
dati nelle due tabelle in relazione?

dai un’occhiata a:

Il 27 gennaio 2009 22.19, Mauro [email protected] ha scritto:

A questo punto ho lanciato script/console e fatto un
@category=Categoyt.create(…) e
@supplier=Supplier.create(…)
Le due tabelle categories e suppliers vengono popolate ma la tabella di join
no.

cosa c’è in quei …?
in particolare, dov’è il dato di quali categorie appartengono a quali
supplier?

@supplier.categories = Category.find(:all)
@supplier.save

oppure:
@category.suppliers = Supplier.first

2009/1/27 Sandro P. [email protected]

Se crei correttamente i due has_and_belongs_to_many la tabella di join si
autopopola :smiley:

Allora ti spiego com’e’ la situazione perche’ la tabella di join non si
e’
autopopolata.
Dunque i modelli sono
class Category < ActiveRecord::Base
belongs_to :sector
has_and_belongs_to_many :suppliers
end

Il belongs to sector e’ perche’ la categoria appartiene ad un settore
specifico, ma il mio problema e’ tra Category e:

class Supplier < ActiveRecord::Base
has_and_belongs_to_many :categories
end

A questo punto ho lanciato script/console e fatto un
@category=Categoyt.create(…) e
@supplier=Supplier.create(…)
Le due tabelle categories e suppliers vengono popolate ma la tabella di
join
no.
La migration per la tabella di join e’:

class CreateCategoriesSuppliersJoin < ActiveRecord::Migration
def self.up
create_table :categories_suppliers, :id => false do |t|
t.references :category, :null => false
t.references :supplier, :null => false
end

def self.down
drop_table :categories_suppliers
end
end
end

Dove sbaglio? A me sembra tutto giusto.

2009/1/27 Pietro G. [email protected]:

@category.suppliers = Supplier.first

scusa:
@category.suppliers = [Supplier.first]

2009/1/27 Pietro G. [email protected]

2009/1/27 Pietro G. [email protected]:

@category.suppliers = Supplier.first

scusa:
@category.suppliers = [Supplier.first]


Ml mailing list
[email protected]
http://lists.ruby-it.org/mailman/listinfo/ml

Quindi la tabella di join non viene popolata automaticamente come hai
detto,
bisogna intevenire col codice o qualcosa mi sfugge?

2009/1/27 Pietro G. [email protected]

Il 27 gennaio 2009 22.19, Mauro [email protected] ha scritto:

A questo punto ho lanciato script/console e fatto un
@category=Categoyt.create(…) e
@supplier=Supplier.create(…)
Le due tabelle categories e suppliers vengono popolate ma la tabella di
join
no.

cosa c’è in quei …?

Ci sono i campi valorizzati @category=Categoyt.create(:id => 1, :codice
=>‘A01’, :descrizione => ‘prova’)
@supplier=Supplier.create(:id => 1, :nome => ‘pingo’, :cognome =>
‘pongo’)

in particolare, dov’è il dato di quali categorie appartengono a quali
supplier?

Dovrebbero essere le foreign keys nella tabella di join no? category_id
e
supplier_id.

2009/1/27 Mauro [email protected]:

Dovrebbero essere le foreign keys nella tabella di join no? category_id e
supplier_id.

andiamo con ordine.
tu hai Supplier e Category, e ogni supplier può appartenere a più
categorie e ogni categoria può appartenere a ogni supplier.

una volta che hai creato una categoria e un supplier, c’è ancora un
dato che manca: questa categoria si applica a questo supplier?

se hai tre categorie e tre suppier, il software non può indovinare
come sono associate.

ci sono diversi modi di procedere, in base a come vuoi che sia
l’interfaccia:

a) puoi inserire prima tutte le categorie, poi tutti i clienti e
infine fare un rettangolo di checkbox in cui spuntare quelle giuste;

(crei gli oggetti così come hai fatto finora, poi fai il form
(eventualmente parliamo di come si può fare) e fai le varie
associazioni;

b) puoi inserire prima le categorie; quando poi inserisci ogni
supplier, scegli con checkbox o una select multipla le categorie per
questo supplier;

c) puoi fare una cosa mista, più complessa ma forse più comoda per
l’utente: quando inserisci una categoria, fai scegliere tra i supplier
già presenti ed eventualmente dei nuovi, e viceversa, quando inserisci
un supplier, fai scegliere tra le categorie.

quasi tutto il lavoro lo fa activerecords da solo: come ti ho scritto,
puoi aggiungere o togliere oggetti direttamente agendo su
@supplier.categories, o @category.suppliers come se fossero array
(anche se un occhio a quello che poi succede in termini di interazione
col database non guasta).

2009/1/27 Pietro G. [email protected]:

se hai tre categorie e tre suppier, il software non può indovinare

b) puoi inserire prima le categorie; quando poi inserisci ogni
supplier, scegli con checkbox o una select multipla le categorie per
questo supplier;

Piano piano comincio a capire grazie anche agli episodi su railscast,
veramente utili :slight_smile:
Ho scelto la soluzione b.
Una parte del codice della view per i suppliers e’:

<%= f.label :aggiudicazioniGare %>
<%= f.text_field :aggiudicazioniGare %>

<%= f.label :note %>
<%= f.text_area :note %>

<%= f.label :categorie %>
<%= f.collection_select :category_ids, @categories, :id, :descrizione %>

Dove quindi ho una select attraverso la quale indicare la categoria
associata al fornitore.
Ovviamente nel controller fornitore ho: @categories=Category.find(:all).
Funziona :slight_smile:
Pero’ mi serve qualcosa di piu’:
Le categorie fanno parte di diversi settori, cioe’ ho un’associazione
uno a molti tra settori e categorie.
Quando inserisco un nuovo fornitore percio’, non voglio assegnarli la
categoria di appartenenza scegliendo tra tutte le categorie
disponibili, bensi’ vorrei prima indicare il settore per poi scegliere
tra le categorie appartenenti al settore selezionato.
Non so se sono riuscito a spiegarmi.
Dovrei capire a fondo i meccanismi che collegano i modelli con i
controllers con le views, e infatti li sto capendo piano piano
lavorando sul campo, solo che non riesco a trovare la soluzione a
questo banale problemino.

2009/2/4 Mauro [email protected]:

Pero’ mi serve qualcosa di piu’:
Le categorie fanno parte di diversi settori, cioe’ ho un’associazione
uno a molti tra settori e categorie.
Quando inserisco un nuovo fornitore percio’, non voglio assegnarli la
categoria di appartenenza scegliendo tra tutte le categorie
disponibili, bensi’ vorrei prima indicare il settore per poi scegliere
tra le categorie appartenenti al settore selezionato.

chiarissimo.

nella mia applicazione ho incontrato questo problema più volte.

in un caso, poiché le opzioni possibili erano veramente tante, ho fatto
così:
una normale select “settore”, poi un observe_field che, quando scatta,
fa una richiesta ajax a un apposito metodo di un controller, che
restituisce la select con le sole categorie per quel settore.

relativamente veloce da scrivere, però, ripeto, consigliabile solo se
il numero di settori e/o categorie è molto molto alto, perché la
richiesta ajax ci impiega un po’.

in un altro caso, le opzioni possibili erano pochi, ho usato
javascript (in una view: era un file .js.erb):

var sections = new Array();
<%- for section in @sections -%>
elem = document.createElement(‘optgroup’);
elem.setAttribute(‘id’, ‘<%= “group#{section.id}” -%>’);
elem.appendChild(create_option(‘’, ‘<%= t(:option_pick_a_user) -%>’));
<% for user in section.users -%>
elem.appendChild(create_option(‘<%= user.id -%>’, ‘<%= user.nick
-%>’));
<% end -%>
sections[‘<%= section.id -%>’] = elem;
<% end -%>

function create_option(value, text) {
o = document.createElement(‘option’);
o.setAttribute(‘value’, value);
o.appendChild(document.createTextNode(text));
return o;
}

function set_users() {
section = $(‘assignment_section_id’).value;
if (section == “”) {
$(‘users’).hide();
$(‘buttons’).hide();
}
else {
sel = $(‘assignment_user_id’);
children = sel.childNodes;
for(i = 0; i < children.length; i++)
sel.removeChild(children[i]);
sel.appendChild(sections[section]);
$(‘users’).show();
$(‘buttons’).show();
}
}

come vedi, ho popolato un array javascript, e poi nella view uso:

<%= observe_field “id_della_select”, :function => “set_users()” %>

forse è possibile creare gli optgroup direttamente da codice erb,
senza usare javascript, crearli tutti invisibili e poi mostrare quello
giusto, ma non ne sono sicuro.

2009/2/5 Pietro G. [email protected]:

nella mia applicazione ho incontrato questo problema più volte.

in un caso, poiché le opzioni possibili erano veramente tante, ho fatto così:

una normale select “settore”, poi un observe_field che, quando scatta,
fa una richiesta ajax a un apposito metodo di un controller, che
restituisce la select con le sole categorie per quel settore.

Tanto per cominciare mi trovo nella banale situazione di inserire
nella view dei fornitori la lista dei settori.
Ancora prima di poter selezionare la categoria di appartenenza del
fornitore devo scegliere il settore, il problema su come poi
visualizzare le categorie relative a quel settore lo risolvo dopo.
Nella view del fornitore, new.html.erb, ho inserito <%=
f.collection_select :sector_id, @sectors, :id, :descr %> ma ovviamente
mi da errore perche’ il metodo sector_id non esiste nella classe
fornitore.
Mi trovo in stand_by per questo banale problema.

2009/2/5 Pietro G. [email protected]:

<% end -%>
function set_users() {
sel.appendChild(sections[section]);
forse è possibile creare gli optgroup direttamente da codice erb,
senza usare javascript, crearli tutti invisibili e poi mostrare quello
giusto, ma non ne sono sicuro.

Ah ecco, piu’ complicato del previsto, agli observe non ci sono ancora
arrivato :slight_smile:

Pietro G. wrote:

<%= observe_field “id_della_select”, :function => “set_users()” %>

Come mai richiami una funzione javascript e non un’azione del
controller?

2009/2/15 Pietro G. [email protected]:

caricarli tutti (in questo caso creando un array javascript) per poi
mostrare quelli giusti. in questo caso, non si chiama nessun’azione
del server.

quando invece i dati sono tanti, allora si preferisce fare una richiesta ajax.

tra parentesi: su quale sia il modo migliore di gestire le richieste
dinamiche ci sono varie scuole. io finora ho usato prototype, ma vedo
che sempre più gente utilizza jquery, che sembra carino, anche se non
l’ho ancora provato.

Ho proprio ieri potuto apprezzare l’utilita’ dell’observ_field.
Per poter utlizzare ajax ho utilizzato, come indicato nel manuale
agile web development with rails, <% javascript_include_tag :defauts
%>.
Non quindi se viene utilizzato jquery o prototype, c’e’ un modo per
capirlo?
Un’altra domanda.
Bene o male nel form di “nuovo fornitore”, in cui va riempito il form
per la creazione di un nuovo fornitore, sono riuscito ad inserire
l’elenco dei settori e, alla scelta di un settore, tramite
l’observ_field, vengono presentate le categorie relative.
Tutto nella stessa pagina.
Se non sbaglio pero’ e’ impossibile fare tutto con un’unica
operazione, nel senso: creare un nuovo fornitore e contemporaneamente
assegnargli le categorie di appartenenza.
Devo prima avere gia’ in tabella l’id del fornitore e solo cosi’ posso
successivamente assegnare tale fornitore a diverse categorie.
Sbaglio?

2009/2/15 Msan M. [email protected]:

Pietro G. wrote:

<%= observe_field “id_della_select”, :function => “set_users()” %>

Come mai richiami una funzione javascript e non un’azione del
controller?

come ti dicevo, non sempre è necessario usare ajax: se i dati tra cui
scegliere sono pochi*, si può prendere in considerazione l’idea di
caricarli tutti (in questo caso creando un array javascript) per poi
mostrare quelli giusti. in questo caso, non si chiama nessun’azione
del server.

quando invece i dati sono tanti, allora si preferisce fare una richiesta
ajax.

tra parentesi: su quale sia il modo migliore di gestire le richieste
dinamiche ci sono varie scuole. io finora ho usato prototype, ma vedo
che sempre più gente utilizza jquery, che sembra carino, anche se non
l’ho ancora provato.

  • cosa vuol dire pochi e molti? non saprei, è difficile stabilirlo,
    dipende da tanti fattori, come la velocità di connessione. non so se
    qualcuno, là fuori, ha proposto un limite.

pietro

2009/2/15 Pietro G. [email protected]:

  • in Provider, crei un attributo virtuale, che so: categories_wannabe;
  • nel form di creazione, passi le categorie con questo nome, ad
    esempio con fields_for :categories_wannabe;
  • sempre in Provider, metti un after_create o un after_save (a seconda
    se ti interessa solo il caso creazione o anche il caso modifica) in
    cui assegni le categorie.

trovi un esempio (anche se un po’ diverso) qui:
#75 Complex Forms Part 3 - RailsCasts

Perfetto grazie mille :slight_smile:

p.s. che intendi per Provider?

Il 15 febbraio 2009 13.19, Mauro [email protected] ha scritto:

2009/2/15 Pietro G. [email protected]:
p.s. che intendi per Provider?

semplicemente il model del fornitore. supplier? provider?

2009/2/15 Pietro G. [email protected]:

Il 15 febbraio 2009 13.19, Mauro [email protected] ha scritto:

2009/2/15 Pietro G. [email protected]:
p.s. che intendi per Provider?

semplicemente il model del fornitore. supplier? provider?

ah ok supplier allora :slight_smile:

2009/2/15 Mauro [email protected]:

Per poter utlizzare ajax ho utilizzato, come indicato nel manuale
agile web development with rails, <% javascript_include_tag :defauts
%>.
Non quindi se viene utilizzato jquery o prototype, c’e’ un modo per capirlo?

rails di default usa prototype.

Se non sbaglio pero’ e’ impossibile fare tutto con un’unica
operazione, nel senso: creare un nuovo fornitore e contemporaneamente
assegnargli le categorie di appartenenza.
Devo prima avere gia’ in tabella l’id del fornitore e solo cosi’ posso
successivamente assegnare tale fornitore a diverse categorie.
Sbaglio?

non è ancora possibile farlo automaticamente (cioè senza sgobbare); lo
sarà presto: nella 2.3 dovrebbe introdurre i nested form, che servono
esattamente a questo.

tuttavia c’è un modo per ottenere il risultato che vuoi.

in breve:

  • in Provider, crei un attributo virtuale, che so: categories_wannabe;
  • nel form di creazione, passi le categorie con questo nome, ad
    esempio con fields_for :categories_wannabe;
  • sempre in Provider, metti un after_create o un after_save (a seconda
    se ti interessa solo il caso creazione o anche il caso modifica) in
    cui assegni le categorie.

trovi un esempio (anche se un po’ diverso) qui:

pietro