PDA

View Full Version : [Ruby On Rails] Prendere dati da un database e visualizzarli in una vista


Xfree
02-02-2009, 19:53
Ciao a tutti!
Ho un problema con il classico esempio Depot del libro Agile Web Development with Rails, per chi non lo conoscesse nel database c'è una tabella prodotti, io ho voluto aggiungere una tabella categorie proprio per distinguerli.

Nel codice del modello del prodotto product.rb ho aggiunto il collegamento alle categorie

class Product < ActiveRecord::Base
[...]
belongs_to :categories


in quello delle categorie category.rb ho fatto altrettanto.

class Category < ActiveRecord::Base
has_many :products
end


Ora vorrei recuperare la categoria a cui appartiene il libro così, sempre nel modello del prodotto product.rb, ho definito

def find_category
@category = Product.find(:all,
:joins => "as p inner join categories as c on p.category_id = c.id",
:select => "c.category")
end


e modificata di conseguenza la vista con la chiamata alla funzione appena definita per visualizzare la categoria accanto il titolo del libro

<h1><%= I18n.t 'main.title' %></h1>
<% for product in @products -%>
<div class="entry">
<%= image_tag(product.image_url) %>
<h3><%=h product.title %> / <%=h product.find_category%></h3>
<%= product.description %>
<span class="price"><%= number_to_currency(product.price) %></span>
<% form_remote_tag :url => { :action => :add_to_cart, :id => product } do %>
<%= submit_tag I18n.t('main.button.add') %>
<% end %>
</div>
<% end %>


Purtroppo il risultato non è quello che mi aspettavo, così come potete vedere dalla seguente immagine.
http://www.pctunerup.com/up/results/_200902/th_20090202204148_CropperCapture1.jpg (http://www.pctunerup.com/up/image.php?src=_200902/20090202204148_CropperCapture1.jpg)

A quanto pare mi viene restituito un array (?), o comunque tre elementi, che corrispondono al numero di libri appartenenti a quella categoria; in effetti eseguendo la query dal prompt SQL ottengo una tabella con tre elementi

http://www.pctunerup.com/up/results/_200902/20090202205147_CropperCapture2.jpg

Tra l'altro vorrei anche creare un menù sulla sinistra con le categorie che linkano ai rispettivi libri, ma se non riesco a risolvere questo problema non saprei come fare.

pierosa
02-02-2009, 20:24
Ciao,
con l'associazione uno a molti col belogs_to devi mettere il singolare

class Product < ActiveRecord::Base
[...]
belongs_to :category

poi se ha una istanza di Product e vuoi sapere la sua categoria basta che fai così:

@product.category

senza fare altre query.

Xfree
02-02-2009, 21:00
Ciao e grazie per la risposta!
Ho modificato l'associazione e cambiato anche la vista cambiando il tag

<%=h product.category%>
facendo così visualizza qualcosa di questo tipo #<Category:0x479b978>, è un problema di casting?
Nel dubbio ho anche commentato la funzione che avevo definito nel modello del prodotto.
Il log sembra ok

Processing StoreController#index (for 127.0.0.1 at 2009-02-02 22:01:29) [GET]
Session ID: 15c18f2c19c685bbb3987578c62a64a6
[4;35;1mProduct Load (0.0ms) [0m [0mSELECT * FROM `products` ORDER BY title [0m
Rendering template within layouts/store
Rendering store/index
[4;36;1mProduct Columns (0.0ms) [0m [0;1mSHOW FIELDS FROM `products` [0m
[4;35;1mCategory Columns (0.0ms) [0m [0mSHOW FIELDS FROM `categories` [0m
[4;36;1mCategory Load (0.0ms) [0m [0;1mSELECT * FROM `categories` WHERE (`categories`.`id` = 1) [0m
[4;35;1mCACHE (0.0ms) [0m [0mSELECT * FROM `categories` WHERE (`categories`.`id` = 1) [0m
[4;36;1mCACHE (0.0ms) [0m [0;1mSELECT * FROM `categories` WHERE (`categories`.`id` = 1) [0m
Rendered store/_cart (0.0ms)
Completed in 47ms (View: 47, DB: 0) | 200 OK [http://localhost/store]

pierosa
02-02-2009, 21:16
@product.category restituisce un oggetto di tipo categoria. #<Category:0x479b978> è la rappresentazione predefinita di un oggetto che indica il nome della classe e un l'id dell'oggeto (non quello di ActiveRecord).
Quello che tu vuoi fare è accedere ad un attributo di un getto categoria, facciamo per esempio che sia description, allora devi fare così:

@product.category.description

Xfree
02-02-2009, 21:40
:cry: Perfetto, funziona, grazie mille!!
Ora sto vedendo come creare dinamicamente un menù con la lista delle categorie linkate ai rispettivi libri.
Avevo pensato di usare un ciclo così composto da inserire nel layout store all'interno di un div

<% for category in @categories -%>
<li>category.category</li>
<%end%>


(so che il nome dell'attributo è infelice :stordita: ) Il problema per me è come creare un link alla rispettive pagine..

Xfree
02-02-2009, 21:45
Così come avevo pensato di fare il ciclo non funziona :mbe:

You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.each

pierosa
02-02-2009, 21:57
Nel controller devi inizializzare la variabile @categories

@categories = Category.all

poi per il menu puoi fare così

<% for category in @categories -%>
<li><%= link_to(h(category.category), category_path(category)) %></li>
<%end%>

pierosa
02-02-2009, 21:58
che edizione hai del libro "Agile Web Development with Rails"?

Xfree
02-02-2009, 22:04
Ho la terza edizione (http://www.pragprog.com/titles/rails3/agile-web-development-with-rails-third-edition) attualmente in versione B1.10. Nel frattempo, controllando i sorgenti, confrontandolo con quello degli altri modelli avevo capito che il problema dipendeva dalla mancata inizializzazione della variabile perché, guardando il codice della classe, era vuoto.
Purtroppo continua a non funzionare, è come se non inizializzasse l'oggetto.
Il modello category.rb è:
class Category < ActiveRecord::Base
has_many :products
@categories = Category.all
end


ma ho provato anche con

class Category < ActiveRecord::Base
has_many :products
def initialize
@categories = Category.all
end

end


che dovrebbe essere corretto.

pierosa
02-02-2009, 22:13
Non devi inizializzarla nel model devi inizializzarla nel controller. Più precisamente nella action corrispondente alla vista dove fai il ciclo.
Comunque ti consiglio di seguire con calma passo passo il libro che hai comprato che è ottimo.

Xfree
02-02-2009, 22:19
Guarda, il controller è stato creato automaticamente ed il codice è il seguente

class CategoriesController < ApplicationController
# GET /categories
# GET /categories.xml
def index
@categories = Category.find(:all)

respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => @categories }
end
end

# GET /categories/1
# GET /categories/1.xml
def show
@category = Category.find(params[:id])

respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => @category }
end
end

# GET /categories/new
# GET /categories/new.xml
def new
@category = Category.new

respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => @category }
end
end

# GET /categories/1/edit
def edit
@category = Category.find(params[:id])
end

# POST /categories
# POST /categories.xml
def create
@category = Category.new(params[:category])

respond_to do |format|
if @category.save
flash[:notice] = 'Category was successfully created.'
format.html { redirect_to(@category) }
format.xml { render :xml => @category, :status => :created, :location => @category }
else
format.html { render :action => "new" }
format.xml { render :xml => @category.errors, :status => :unprocessable_entity }
end
end
end

# PUT /categories/1
# PUT /categories/1.xml
def update
@category = Category.find(params[:id])

respond_to do |format|
if @category.update_attributes(params[:category])
flash[:notice] = 'Category was successfully updated.'
format.html { redirect_to(@category) }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => @category.errors, :status => :unprocessable_entity }
end
end
end

# DELETE /categories/1
# DELETE /categories/1.xml
def destroy
@category = Category.find(params[:id])
@category.destroy

respond_to do |format|
format.html { redirect_to(categories_url) }
format.xml { head :ok }
end
end
end



L'inizializzazione a cui ti riferisci è quella del 5° rigo?

pierosa
02-02-2009, 22:22
si
ma tu questo codice dove lo hai messo?

<% for category in @categories -%>
<li>category.category</li>
<%end%>

Xfree
02-02-2009, 22:33
La vista app/view/categories/index.html.erb ,generata automaticamente con il seguente codice,

<h1>Listing categories</h1>

<table>
<tr>
<th>Category</th>
</tr>

<% for category in @categories %>
<tr>
<td><%=h category.category %></td>
<td><%= link_to 'Show', category %></td>
<td><%= link_to 'Edit', edit_category_path(category) %></td>
<td><%= link_to 'Destroy', category, :confirm => 'Are you sure?', :method => :delete %></td>
</tr>
<% end %>
</table>

<br />

<%= link_to 'New category', new_category_path %>


funziona alla perfezione.
L'applicazione funziona con l'indirizzo http://localhost:3000/store, la visualizzazione della categoria accanto al titolo del libro l'ho messa nella vista
app/view/store/index.html.erb e funziona anche questa.


<!-- START_HIGHLIGHT -->
<h1><%= I18n.t 'main.title' %></h1>
<!-- END_HIGHLIGHT -->

<% for product in @products -%>
<div class="entry">
<%= image_tag(product.image_url) %>
<h3><%=h product.title %> / <%=h product.category.category%></h3>
<%= product.description %>
<span class="price"><%= number_to_currency(product.price) %></span>
<!-- START:form_remote_tag -->
<% form_remote_tag :url => { :action => :add_to_cart, :id => product } do %>
<!-- START_HIGHLIGHT -->
<%= submit_tag I18n.t('main.button.add') %>
<!-- END_HIGHLIGHT -->
<% end %>
<!-- END:form_remote_tag -->
</div>
<% end %>


La costruzione del menù, così come volevo farla io nell'homepage, l'ho messa nel layout app/view/layouts/store.html.erb all'interno del div con classe sidebar.

[...]
<div id="page">
<div class="bgtop">
<!-- start content -->
<div id="content">
<!-- START:flash -->
<% if flash[:notice] -%>
<div id="notice"><%= flash[:notice] %></div>
<% end -%>
<!-- END:flash -->
<div id="store">
<%= yield :layout %>
</div>
</div>
<!-- end content -->
<!-- start sidebar -->
<div id="sidebar">

<!-- START:hidden_div -->
[...]

Evidentemente non ho capito qualcosa a proposito del funzionamento e mi sfugge qualcosa a proposito della visibilità degli oggetti.

pierosa
02-02-2009, 22:42
se l'hai messa nel layout allora deve essere disponibile a tutte le viste che usano quel layout (probabilmente tutte) per cui devi inizializzarla in tutte le action. Puoi usare anche i filtri ma sono un argomento più avanzato.

Se hai bisogno di altro mi ritrovi damani ora vado a letto.
Ciao

pierosa
02-02-2009, 22:44
Evidentemente non ho capito qualcosa a proposito del funzionamento e mi sfugge qualcosa a proposito della visibilità degli oggetti.
Guarda all'inizio ci vuole un pò per capire il meccanismo, ma come ti ho detto prima se segui il tutorial passo per passo poi ti studi i capitoli di approfondimento sui vari argomenti vedrai che non è così difficile anzi...
Ciao

Xfree
02-02-2009, 22:46
Quindi dovrei inizializzare


@categories = Category.find(:all)


in tutte le action del controller categories, se ho capito bene.
Io continuo un altro pò, poi vado a letto anche io.
Grazie per le risposte e per il tempo dedicatomi.

Xfree
02-02-2009, 23:05
Le migliori illuminazioni vengono quando ormai si perdono le speranze :asd:
Ho pensato un attimo e mi sono detto che se era lo store l'oggetto visualizzato, allora dovevo inizializzare la variabile nell'index del controller dello store, così ho fatto ed ha funzionato, come puoi vedere dall'immagine.
http://www.pctunerup.com/up/results/_200902/th_20090202235632_CropperCapture3.jpg (http://www.pctunerup.com/up/image.php?src=_200902/20090202235632_CropperCapture3.jpg)

Cliccando sul link ottengo il seguente errore che vedrò di sistemare domani, anche perché ormai il sonno avanza inesorabile! :ronf:
http://www.pctunerup.com/up/results/_200902/th_20090203000324_CropperCapture4.jpg (http://www.pctunerup.com/up/image.php?src=_200902/20090203000324_CropperCapture4.jpg)

Eh sì, ora o funziona il frontend o funziona il backend, per accedere al backend devo rimuovere

<% for category in @categories -%>
<li><%= link_to(h(category.category), category_path(category)) %></li>
<%end%>

dal layout app/views/layouts/store.html.erb

Andrea2084
10-09-2010, 14:29
Salve,
mi rendo conto che il thread risale a più di un anno fa. Anche io ho seguito passo passo il libro "Agile web development with rails" e come te mi piacerebbe creare un menù con la lista delle categorie linkate ai rispettivi libri.
Cioè vorrei che, una volta che clicco il link di una categoria, la vista index di store mi visualizzasse i prodotti appartenenti a quella categoria. Ho seguito i suggerimenti di questo thread però poi non sono riuscito a implementare ciò che ho appena descritto. Tu come hai fatto?

Premetto che sono alle prime armi con ruby on rails, io mi sono creato il menù delle categorie così come ho letto qui, piazzandolo nel layout "store.html.erb"

<% for category in @categories -%>
<li><%= link_to(h(category.description), :controller => :store, :action => :index, :id => category) %></li>
<%end%>


All'interno del link_to ho specificato ":action => :index" e ":id => category" perchè vorrei che, quando clicco su di una categoria di prodotti, mi si visualizzi la vista index di store ossia l'elenco dei prodotti corrispondenti a quella categoria.
Allora nello store_controller ho pensato di fare così:

def index

@categories = Category.find(params[:id])
@products = Product.find(:all, :order => "articolo", :conditions => ['category_id = ?', @categories.id])
@cart = find_cart

end


In una variabile di istanza acquisisco l'oggetto category e poi faccio estrarre dalla tabella prodotti, soltanto i prodotti per i quali è verificata l'uguaglianza tra la chiave esterna ("category_id") e la chiave primaria della categoria (@categories.id).
Purtroppo al momento di eseguire l'applicazione mi dà errore nel layout "store.html.erb", precisamente nel ciclo "for" del seguente blocco di codice:

<% for category in @categories -%>
<li><%= link_to(h(category.description), :controller => :store, :action => :index, :id => category) %></li>
<%end%>


L'errore visualizzato è: undefined method `each' for #<Category:0x6234438>

Spero che tu mi possa aiutare.

Xfree
10-09-2010, 17:00
Ciao Andrea, mi spiace non poterti essere d'aiuto ma Ruby l'ho rimosso dalla mia mente pochi giorni dopo l'utimo messaggio di questo thread e non ne ricordo più il funzionamento perché poi non l'ho più usato. Tra l'altro non posso nemmeno passarti i file del progetto : ai tempi non l'ho potuto completare perché si ruppe l'hard disk sul quale lo tenevo e non avevo copie di backup (mea culpa :muro: ).

Andrea2084
10-09-2010, 20:48
Ciao Andrea, mi spiace non poterti essere d'aiuto ma Ruby l'ho rimosso dalla mia mente pochi giorni dopo l'utimo messaggio di questo thread e non ne ricordo più il funzionamento perché poi non l'ho più usato. Tra l'altro non posso nemmeno passarti i file del progetto : ai tempi non l'ho potuto completare perché si ruppe l'hard disk sul quale lo tenevo e non avevo copie di backup (mea culpa :muro: ).

Capisco..e vabbè ci ho provato. Vorrà dire che continuerò a spremermi le meningi oppure lascio perdere pure io e mi accontento di un semplice catalogo prodotti. Grazie lo stesso per la risposta.

Nessun'altro può darmi una mano?