Problema relazioni Molti a Molti

Buonasera a tutti, sono un nuovo utente che si sta affacciando al mondo
di Ruby e ho qualche problema che spero mi aiutiate a risolvere…
Ho diverse tabelle che servono a reppresentare una libreria on-line con
libri autori, utenti e whish list.
Ho due relazioni molti a molti Libro<>Autore e Libro<>Whishlist<>Utente.
Per la prima relazione non ho problemi a gestirla, mentre la secondo mi
sta dando delle noie…

Le tabelle del db interessate sono:
CREATE TABLE authors (
id int(11) NOT NULL auto_increment,
name varchar(255) default NULL,
nationality varchar(255) default NULL,
description text,
born int(11) default NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;

CREATE TABLE books (
id int(11) NOT NULL auto_increment,
title varchar(255) default NULL,
description text,
quantity int(11) default NULL,
year int(11) default NULL,
date date default NULL,
min_quantity int(11) default NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=2 ;

CREATE TABLE whishlists (
id int(11) NOT NULL auto_increment,
date date default NULL,
book_id int(11) NOT NULL,
user_id int(11) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=2 ;

Ed i rispettivi modelli:

class User < ActiveRecord::Base
has_many :whishlists, :order => “date”
end

class Whishlist < ActiveRecord::Base
belongs_to :book, :foreign_key => “bookId”
belongs_to :user, :foreign_key => “userId”
end

class Book < ActiveRecord::Base
validates_presence_of :title
validates_presence_of :description
validates_numericality_of :quantity
validates_numericality_of :year
validates_numericality_of :min_quantity
validates_presence_of :date
has_and_belongs_to_many :authors
has_many :whishlists , :order => “date”
end

Il Controllore di user nell’azione incriminata:
def show
@user = User.find(params[:id])
@whish= Whishlist.find(:all, :conditions=>[“user_id
=?”,params[:id]])
end

Ed Infine la vista:

<% for column in User.content_columns %>

<%= column.human_name %>: <%=h @user.send(column.name) %>

<% end %>

<%= link_to ‘Edit’, :action => ‘edit’, :id => @user %> |
<%= link_to ‘Back’, :action => ‘list’ %>

WhishList

<% for book in @whish.books%> <%end%>
<%= book.title%>

Vorrei estrarre tutte le whish legate ad un utente visualizzandone il
titolo del libro.

Continuo a ricevere il seguente errore, chiamando l’azione show con id 1
Showing app/views/users/show.rhtml where line #13 raised:

undefined method `books’ for #Array:0x3469288

Qualcuno riesce a farmi capire dove sbaglio?
Grazie mille!

Andrea M. wrote:

@whish= Whishlist.find(:all, :conditions=>["user_id

=?",params[:id]])

Qui @whish è un array di Whishlist quindi credo tu debba fare :

<% for wish in @whish %>
<% @books = wish.books %>
<% for book in @books %>

<%= book.title%> <% end %> <% end %>

Spero di esserti stato d’aiuto, mi pare che questa soluzione funzioni.

Francesco B. wrote:

Andrea M. wrote:

@whish= Whishlist.find(:all, :conditions=>["user_id

=?",params[:id]])

Qui @whish è un array di Whishlist quindi credo tu debba fare :

<% for wish in @whish %>
<% @books = wish.books %>
<% for book in @books %>

<%= book.title%> <% end %> <% end %>

Spero di esserti stato d’aiuto, mi pare che questa soluzione funzioni.

Ciao Francesco,
ho provato la modifica da te suggerita, ma mi viene presentato sempre
un errore, e più precisamente:

undefined method `books’ for #Whishlist:0x354bebc
Extracted source (around line #14):

11:

WhishList


12:
13: <% for wish in @whish %>
14: <% @books = wish.books %>
15: <% for book in @books %>
16:
17:

Non vorrei aver istanziato male i modelli e quindi non riesce a capire
che book appartiene a whish…

<%= book.title%>

Nel tuo modello ogni wishlist ha un libro solo perché hai usato
belongs_to :book. Per avere una relazioni molti a multi o devi usare
has_and_belongs_to_many o invece has_many :through

Credo che has_and_belongs_to_many sia più semplice da usare ma un
svantaggio principile è che non si puoi aggiungere altri
attributi/informazioni tipo se il libro è importante per l’utente o se
è stato comprato ecc.

Per usare has_and_belongs_to_many devi creare una tabella ‘join’ che
dev’essere chiamato “books_whishlists”. Così nel modello books puoi
scrivere has_and_belongs_to_many :whishlists e nel modello whishlists
puoi scrivere has_and_belongs_to_many :books

La tabella books_whishlists contiene i due ID:

create table “books_whishlists” do |t|
t.column “book_id”, :integer
t.column “whishlist_id”, :integer
end

Ovvio, non serve più il campo book_id nel tabella whishlists.

Non sono stato chiaro o forse non ho capito bene :wink: Quando parlo degli
attributi non intendevo il tuo wishlist. Sul amazon (per esempio) puoi
aggiungere una priorità o un commento per ogni libro ma questi
informazioni fanno parte della relazioni e non vengono salvati nella
tabella dei libri o la tabella dei wishlist ma nel tabella di “join” e
in questo caso devi usare has_many :through

class Wishlist < ActiveRecord::Base
has_many :wishes
has_many :books, :through => :wishes
end

class Book < ActiveRecord::Base
has_many :wishes
has_many :wishlists, :through => :wishes
end

class Wish < ActiveRecord::Base
belongs_to :book
belongs_to :wishlist
end

create_table “wishes”
t.column :book_id, :integer
t.column :subject_id, :integer
t.column :created_at, :datetime
t.column :updated_at, :datetime
t.column :comment, :string
end

Rob C. wrote:

Non sono stato chiaro o forse non ho capito bene :wink: Quando parlo degli
attributi non intendevo il tuo wishlist. Sul amazon (per esempio) puoi
aggiungere una priorità o un commento per ogni libro ma questi
informazioni fanno parte della relazioni e non vengono salvati nella
tabella dei libri o la tabella dei wishlist ma nel tabella di “join” e
in questo caso devi usare has_many :through

Più che altro forse non mi sono espresso bene io… :smiley:
A quanto vedo, devo variare la mia attuale situazione modificando i due
modelli con la clausola through… ma a livello di controllore e vista
posso mantenere l’attuale struttura?

L’esempio Amazon che hai riportato è proprio quello che vorrei fare come
esercizio per comprendere bene questo tipo di relazioni…

grazie mille!

Rob C. wrote:

Credo che has_and_belongs_to_many sia pi� semplice da usare ma un
svantaggio principile � che non si puoi aggiungere altri
attributi/informazioni tipo se il libro � importante per l’utente o se
� stato comprato ecc.

Per usare has_and_belongs_to_many devi creare una tabella ‘join’ che
dev’essere chiamato “books_whishlists”. Cos� nel modello books puoi
scrivere has_and_belongs_to_many :whishlists e nel modello whishlists
puoi scrivere has_and_belongs_to_many :books

La tabella books_whishlists contiene i due ID:

create table “books_whishlists” do |t|
t.column “book_id”, :integer
t.column “whishlist_id”, :integer
end

Ovvio, non serve pi� il campo book_id nel tabella whishlists.

Io nella tabella whish necessito anche di altre informazioni.
La relazione con has_and_belongs_to_many l’ho usata per creare la
relazione tra autori e libri, e funziona che è una meraviglia…Avevo
provato ad utilizzare has_many :through ma avevo riscontrato dei
problemi (per mia incapacità )…

Quindi ragazzi mi dite che devo utilizzare a tal proposito la has_many
con clausola :through?
Se non è un problema potreste spiegarmi molto velocemente come gestire
tale clausola nel controllore e nella vista? ho provato a cercare un po’
nella rete ma non ho capito molto… distorsione di cakephp :smiley:

Devi modificare i due modelli e aggiungere quello nuovo con anche una
tabella nel db. Per visualizzare i libri di un wishlist puoi usare
quello che hai scritto. Per la modificare puoi fare:

Per aggiungere un libro a un wishlist
wishlist.books << book

Per cancellare un libro da un wishlist
wishlist.books.delete book

Rob C. wrote:

Devi modificare i due modelli e aggiungere quello nuovo con anche una
tabella nel db. Per visualizzare i libri di un wishlist puoi usare
quello che hai scritto. Per la modificare puoi fare:

Per aggiungere un libro a un wishlist
wishlist.books << book

Per cancellare un libro da un wishlist
wishlist.books.delete book

Niente da fare… non riesco a fare andare il tutto…

Ho eliminato a questo punto whishlist perchè secondo me logicamente
ridondante ed inutile e ho i seguenti modelli:
class User < ActiveRecord::Base
has_many :wishes
has_many :books, :through => :wishes
end

class Book < ActiveRecord::Base
validates_presence_of :title
validates_presence_of :description
validates_numericality_of :quantity
validates_numericality_of :year
validates_numericality_of :min_quantity
validates_presence_of :date
has_and_belongs_to_many :authors
has_many :wishes
has_many :users, :through => :wishes
end

class Whish < ActiveRecord::Base
belongs_to :book
belongs_to :user
end


Il seguente controllore(azione che richiamo) (appartiene al controllore
user):

def show
@user = User.find(params[:id])
@whish= Whish.find(:all, :conditions=>[“user_id =?”,params[:id]])
end


E la seguente vista:

% for column in User.content_columns %>

<%= column.human_name %>: <%=h @user.send(column.name) %>

<% end %>

<%= link_to ‘Edit’, :action => ‘edit’, :id => @user %> |
<%= link_to ‘Back’, :action => ‘list’ %>

WhishList

<% for whish in @whish %> <% @books = whish.books %> <% for book in @books %> <% end %> <% end %>
<%= book.title%>

ricevo sempre questo errore:

howing app/views/users/show.rhtml where line #14 raised:

undefined method `books’ for #Whish:0x364ea30
Extracted source (around line #14):

11:

WhishList


12:
13: <% for whish in @whish %>
14: <% @books = whish.books %>
15: <% for book in @books %>
16:
17:

Se la tabella whishes è vuota non vengono sollevati errori in quanto non
effettua il ciclo for della riga 14 altrimenti mi genera l’errore che ho
riportato.

Non capisco davvero…

grazie

<%= book.title%>

è più facile giocare con i modelli usando il console

ruby script/console

user = User.find(:first)
user.books
user.wishes[0].books

user.wishes.create(:book => Book.find(:first), :comment => “prova”)

Devi scegliere se chiamare il modello Whish o Wish e dopo usare lo
stesso nome nel has_many :through.

Per vedere tutti i libri di un utente basta usare l’oggetto del
utente. Per accedere a tutti i libri di un utente basta fare
@user.books e invece per accedere a tutti i “wish” di un utente (libro
più commento, priorità, ecc.) basta fare @user.wishes

def show
@user = User.find(params[:id])
end

WhishList

<% for wish in @user.wishes %> <% end %>
<%= wish.book.title%> <%= wish.comment %>

Rob C. wrote:

user.wishes.create(:book => Book.find(:first), :comment => “prova”)

Cavoli avevo fatto delle cappellate di sintassi e altri errori!

allora adesso riesco ad assciare un libro ad un utente con la tabella
whish (quello che mi serve)

con la seguente action:
def addWhish
book = Book.find(params[:id])
book.whishes.create(:user => User.find(params[:user]), :comment =>
“prova”)
redirect_to :action => ‘view’
end

Se volessi a questo punto elimanare l’associazione, ovvero il record in
whishes? Ho provato con la sintassi che mi avevi precedentemente
suggerito ma nada…

Altra domandina… se adesso volessi fare la stessa cosa ma con il
carrello della spesa, come faccio per diminuire la quantità di un libro?

grazie mille!

Ps. ottima la console! devo dire che è molto utile per smanettare sui
modelli

Rob C. wrote:

Hmm non mi riccordo mai ma dovrebbe funzionare
cos�:
book = Book.find(params[:book_id])
user = User.find(params[:user_id])

user.books.delete book
Funziona perfettamente! :smiley:

In che senso? Quando accedi ad un libro puoi sempre cambiare gli
attributi.

user = User.find(params[:user_id])
book = user.books.first

book.quantity = book.quantity - 1
book.save

o book.update_attribute(:quantity, book.quantity - 1)

Facendo così a questo punto mi evito un’ulteriore entità , giusto? In
sosta la mia whish list mi fa sia da carrello che da lista degli oggetti
che osservo, giusto? quindi potrei mettere un campo buyed che mi indica
se è stato comperato e quantity per definirne la quantità che ho
comperato… giusto?

grazie mille! :wink:

On 07/06/07, Andrea M. [email protected] wrote:

attributi.
sosta la mia whish list mi fa sia da carrello che da lista degli oggetti
che osservo, giusto? quindi potrei mettere un campo buyed che mi indica
se è stato comperato e quantity per definirne la quantità che ho
comperato… giusto?

Sì, puoi farlo così anche se sarebbe meglio in realtà separare un
“wish list” da un carrello. Ma visto che stai provando ad imparare RoR
credo che vada bene così.

On 07/06/07, Andrea M. [email protected] wrote:

book = Book.find(params[:id])
book.whishes.create(:user => User.find(params[:user]), :comment =>

“prova”)
redirect_to :action => ‘view’
end

Se volessi a questo punto elimanare l’associazione, ovvero il record in
whishes? Ho provato con la sintassi che mi avevi precedentemente
suggerito ma nada…

Hmm non mi riccordo mai ma dovrebbe funzionare
così:
book = Book.find(params[:book_id])
user = User.find(params[:user_id])

user.books.delete book

Altra domandina… se adesso volessi fare la stessa cosa ma con il
carrello della spesa, come faccio per diminuire la quantità di un libro?

In che senso? Quando accedi ad un libro puoi sempre cambiare gli
attributi.

user = User.find(params[:user_id])
book = user.books.first

book.quantity = book.quantity - 1
book.save

o book.update_attribute(:quantity, book.quantity - 1)

grazie mille!

Ps. ottima la console! devo dire che è molto utile per smanettare sui
modelli

:slight_smile: sono d’accordo

Rob C. wrote:

Sì, puoi farlo così anche se sarebbe meglio in realtà separare un
“wish list” da un carrello. Ma visto che stai provando ad imparare RoR
credo che vada bene così.

Concordo con il tuo parere…

Perdonami se chiedo ancora aiuto però, ho provato quanto da te
suggerito, però non mi riesce

user = User.find(params[:user_id])
book = user.books.first

book.quantity = book.quantity - 1 // in questo caso mi dice che non è
supportato il metodo quantity
book.save

o book.update_attribute(:quantity, book.quantity - 1) // e in questo

che non è supportato il metodo update_attribute

idee?

grazie!

On 08/06/07, Andrea M. [email protected] wrote:

user = User.find(params[:user_id])
book = user.books.first

book.quantity = book.quantity - 1 // in questo caso mi dice che non è
supportato il metodo quantity
book.save

o book.update_attribute(:quantity, book.quantity - 1) // e in questo

che non è supportato il metodo update_attribute

idee?

Hai provato con:
book = Book.find(:first)
book.quantity = 3
book.save

Se non funziona vuol dire che la tabella nel db non ha il campo.

Altrimenti se funziona prova a vedere cos’è il book

user = User.find(params[:user_id])
book = user.books.first

p book.class
p book

Rob C. wrote:

On 08/06/07, Andrea M. [email protected] wrote:

Ciao, ho provato a smanettare un po’ ed utilizzando la console ho
ottenuto i seguenti risultati:

user=User.find(1)
=> #<User:0x3340f8c @attributes={“name”=>“Andrea M.”,
“username”=>“martignoni.a”, “mail”=>“[email protected]”, “id”=>“1”,
“password”=>“andrea”, “address”=>“Via Crocetta 155, 21044 Cavaria
(VA)”}>
book=user.books.first
=> #<Book:0x36b91a0 @attributes={“title”=>“Prova Libro”,
“date”=>“2007-06-07”, “quantity”=>“1”, “id”=>“2”, “min_quantity”=>“23”,
“description”=>“ladfhadfhòdf\r\nsdfjòsdjf\r\nfasòdòflasdj”,
“year”=>“1234”}>
p book.class
Book
=> nil
p book
#<Book:0x36b91a0 @attributes={“title”=>“Prova Libro”,
“date”=>“2007-06-07”, “quantity”=>“1”, “id”=>“2”, “min_quantity”=>“23”,
“description”=>“ladfhadfhòdf\r\nsdfjòsdjf\r\nfasòdòflasdj”,
“year”=>“1234”}>
=> nil

Mentre le seguenti istruzioni:
book = Book.find(:first)
book.quantity = 3
book.save
Funzionano benissimo.

Chiaramente però assegnano la quantità 3 alla tabella book e non whish.

a questo punto come devo fare ad aggiornare la quantità della whish
associata all’utente?
Ho pensato ad un’istruzione del tipo: user.books.quantity=X ma mi da un
errore.

Altro problema che ho notato: quando cancello la whishlist di un utente
con il set di istruzioni:
book = Book.find(params[:id])
user = User.find(params[:user])
user.books.delete book

Ma dopo aver eseguito la seguente azione il recorset nel db diventa
così:
id book_id user_id created_at buyed quantity
19 56 NULL 2007-06-11 11:03:48 NO NULL

Ovvero associa NULL allo user_id. Effettivamente la cancellazione
avviene ma solo logica e non fisica del recordset. Mi domando quindi se
è possibile fare una cancellazione anche fisica…

Grazie!

On 11/06/07, Andrea M. [email protected] wrote:

(VA)"}>
“date”=>“2007-06-07”, “quantity”=>“1”, “id”=>“2”, “min_quantity”=>“23”,

Chiaramente però assegnano la quantità 3 alla tabella book e non whish.

a questo punto come devo fare ad aggiornare la quantità della whish
associata all’utente?
Ho pensato ad un’istruzione del tipo: user.books.quantity=X ma mi da un
errore.

Praticamente è la stessa cosa:
wish = user.wishes.first
wish.quantity = X
wish.save

o invece se vuoi trovare il “wish” per un utente e un libro:
wish = user.wishes.find_by_book_id(ID_DEL_LIBRO)
wish.quantity = X
wish.save

Ovvero associa NULL allo user_id. Effettivamente la cancellazione
avviene ma solo logica e non fisica del recordset. Mi domando quindi se
è possibile fare una cancellazione anche fisica…

Non l’ho provato ma forse devi fare:

class User
has_many :wishes, :dependent => :destroy
end