DB view

Avrei bisogno di creare l’equivalente di una vista db estendendo un
model con una serie di campi “calcolati” da altri model.
Sono riucito a farlo mediante l’aggiunta di un def ad un model
esistente. Il campo custom viene valorizzato correttamente, ma non
riesco ad usarlo come se fosse un vero e proprio campo, con chiamate
tipo:
Mymodel.find(:all, :order => “customfield desc”)

Sapreste suggerirmi una soluzione ?


FleX
[Linux User #347703 PGP Key ID: 98AA9D3E
FingerPrint: 7D25B 0CE4 898A 22CB F765 E2A5 88B7 4C5C 98AA 9D3E]

Devi produrre i campi aggiuntivi, quelli che chiami ‘calcolati’, nel
database, tramite una vista SQL oppure l’aggiunta di colonne calcolate
tramite il metodo select di AR.

Esempio

Mymodel.select(“a, b, a + b AS c”)

In questo caso la colonna “c” non esiste veramente nel database, ma
l’avrai
comunque tra gli attributi di ogni istanza di AR.

Per l’ordinamento a questo punto puoi fare:

Mymodel.select(“a, b, a + b AS c”).order(“(a + b) DESC”)

E’ l’unico modo che hai per poter ottenere i record ordinati
direttamente
dalla query SQL, cosa che ti consiglio caldamente di perseguire sempre e
comunque. Perch, per dirne una, ti consente di avere una paginazione con
una delle numerose gem tipo will_paginate.

2013/11/10 FleX [email protected]

On 11/10/2013 07:19 PM, Fabrizio R. wrote:

Devi produrre i campi aggiuntivi, quelli che chiami ‘calcolati’, nel
database, tramite una vista SQL
associando un model per la vista ?

oppure l’aggiunta di colonne calcolate
A questo ci avevo pensato, ma non volevo inserire query in sql molto
complesse all’interno del controller dato che al momento ero riuscito a
manipolare tutti i dati usando i metodi.


FleX
[Linux User #347703 PGP Key ID: 98AA9D3E
FingerPrint: 7D25B 0CE4 898A 22CB F765 E2A5 88B7 4C5C 98AA 9D3E]

Infatti non lo devi fare nel controller. Consiglio di fare un metodo di
classe che restituisce una relation, o uno scope.

class Mymodel

def self.my_select
select(“a,b, a + b AS c”).order(“(a+b) DESC”)
end

end

Mymodel.my_select.count
Mymodel.my_select.limit(10)
@my_collection = Mymodel.my_select

etc etc etc.

2013/11/10 FleX [email protected]

On 11/10/2013 08:56 PM, Fabrizio R. wrote:

Infatti non lo devi fare nel controller. Consiglio di fare un metodo di
classe che restituisce una relation, o uno scope.

class Mymodel

def self.my_select
select(“a,b, a + b AS c”).order("(a+b) DESC")
end

end

Ottimo, ci sono riuscito, ma senza far a meno di usare RAW sql. Sotto il
mio metodo:

def self.best_avg_prices(n)
self.find_by_sql "SELECT mymodel.*, avg(prices.rating) as avgrating
FROM mymodel, prices WHERE mymodel.id = prices.mymodel_id GROUP
BY mymodel.id ORDER BY avgrating DESC LIMIT " + n.to_s + “;”
end

(Sono sicuro che e’ migliorabile)

FleX
[Linux User #347703 PGP Key ID: 98AA9D3E
FingerPrint: 7D25B 0CE4 898A 22CB F765 E2A5 88B7 4C5C 98AA 9D3E]

AAHHHHRRGGGG!

Ma sei impazzito???!! :slight_smile:

BY mymodel.id ORDER BY avgrating DESC LIMIT + n.to_s + ;"

Mai interpolare le stringhe SQL con delle variabili in quel modo, a meno
che non sei strasicuro della loro fonte.
In generale meglio tenere vicino allinterpolazione anche la
dichiarazione di controllo sulla variabile, cos sicuro che non te la
perdi.
Se per esempio n un valore che prendi da params e passi dentro cos com
ti sei calato praticamente le braghe dal punto di vista della sicurezza.
SQL injection.

In data lunedì 11 novembre 2013 11:25:43, Fabrizio R. ha scritto:

da params e passi dentro così com’è ti sei calato praticamente le braghe
http://lists.ruby-it.org/mailman/listinfo/ml
dove n è “1 UNION ALL SELECT username, password, whatever FROM users;”
:stuck_out_tongue:

On 11/11/2013 11:25 AM, Fabrizio R. wrote:

Mai interpolare le stringhe SQL con delle variabili in quel modo, a meno che non
sei strasicuro della loro fonte.

tranquilli: la variabile viene valorizzata staticamente nel controller,
non ha niente a che vedere con params.
A dirla tutta l’ho messa nel model perche’ quando al metodo appendevo un
.limit(n) mi restituiva un bel undefined method `limit’ for #<Array
e non volevo fare ulteriori conversioni.


FleX
Success is the maximum utilization of the ability that you have
[Linux User #347703 PGP Key ID: 98AA9D3E
FingerPrint: 7D25B 0CE4 898A 22CB F765 E2A5 88B7 4C5C 98AA 9D3E]

Non sei obbligato ad usare i metodi di AR tipo select, limit, joins etc
anche se spesso preferibile.
Ad ogni modo, anche se sei strasicuro della sorgente dei quella
variabile io ci applicherei uno dei tanti filtri che AR usa
internamente, anche solo come esercizio.

http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/DatabaseStatements.html#method-i-sanitize_limit

santizie_limit(n)

(non ricordo se richiede un namespace, ma ci siamo capiti)

On 11/11/2013 11:58 AM, Fabrizio R. wrote:

Non sei obbligato ad usare i metodi di AR tipo select, limit, joins etc anche se
spesso preferibile.
Ad ogni modo, anche se sei strasicuro della sorgente dei quella variabile io ci
applicherei uno dei tanti filtri che AR usa internamente, anche solo come
esercizio.

http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/DatabaseStatements.html#method-i-sanitize_limit

santizie_limit(n)

(non ricordo se richiede un namespace, ma ci siamo capiti)

sicuramente non fa male, modifica fatta .
Grazie come al solito per i preziosi aiuti.


FleX
Success is the maximum utilization of the ability that you have
[Linux User #347703 PGP Key ID: 98AA9D3E
FingerPrint: 7D25B 0CE4 898A 22CB F765 E2A5 88B7 4C5C 98AA 9D3E]

On 11/11/2013 08:08 PM, Luca M. wrote:

scope :best_avg_price, select(“mymodel.*, avg(prices.rating) as
avgrating”).joins(:prices).group(“mymodel.id”)

Perfetto, questo e’ molto piu’ versatile e pulito della soluzione
precedente (unico lieve difetto e’ che non restistuisce il count)
Interessanti gli scope: non li avevo mai utilizzati


FleX
Success is the maximum utilization of the ability that you have
[Linux User #347703 PGP Key ID: 98AA9D3E
FingerPrint: 7D25B 0CE4 898A 22CB F765 E2A5 88B7 4C5C 98AA 9D3E]

2013/11/11 FleX [email protected]

A dirla tutta l’ho messa nel model perche’ quando al metodo appendevo un
.limit(n) mi restituiva un bel undefined method `limit’ for #<Array
e non volevo fare ulteriori conversioni.

il pregio di una soluzione fatta attraverso uno scope rispetto al
cablare
la query SQL in un metodo e’ proprio che potresti poi usare tutti gli
altri
metodi di active record. Senza sapere di preciso il tuo data model, a
occhio:

def self.best_avg_prices(n)
self.find_by_sql "SELECT mymodel.*, avg(prices.rating) as avgrating
FROM mymodel, prices WHERE mymodel.id = prices.mymodel_id GROUP
BY mymodel.id ORDER BY avgrating DESC LIMIT " + n.to_s + “;”
end

potrebbe tradursi in un:

scope :best_avg_price, select(“mymodel.*, avg(prices.rating) as
avgrating”).joins(:prices).group(“mymodel.id”)

nell-ipotesi che MyModel has_many :prices

altrimenti potresti sostituire la chiamata a joins con joins(%{join
prices
on prices.mymodel_id = mymodel.id})

nota bene come lo scope NON include la order by e NON include la limit
perche’ poi lo userai soci:

MyModel.best_avg_price.order(“avgrating DESC”).limit(n)

N.B. il codice e’ solo per dare un’idea l’ho scritto al volo eh :slight_smile:

ciao,
Luca