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”)
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.
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.
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
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;”
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.
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.
(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]
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]
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: