Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Access SQL computed columns through ActiveRecord

I have a Person model, which includes a property representing the data of birth (birth_date).

I also have a method called age(), which works out the current age of the person.

I now have need to run queries based on the person's age, so I have replicated the logic of age() as a computed column in MySQL.

I cannot workout how I would make this additional column part of the default select statement of the model.

I would like to be able to access the age as if it were a native property of the Person model, to perform queries against it and access the value in my views.

Is this possible, or am barking up the wrong tree?

I thought I might be able to define additional fields through default_scope or scope, but these methods seem to only recognise existing fields. I also tried default_scope in tandem with attr_assessor.

Possible workarounds I've considered but would prefer not to do:

  • Create an actual property called age and populate through the use of callbacks. The date is always changing, so this obviously would be be reliable.

  • Replicate the logic in ActiveRecord as age() and in a scope as a where cause. This would achieve what I need, but doesn't feel very DRY.

  • I am already caching the results of the age() method. it is the ability to use the field in where clauses that I am most interested in.

There must be a way to define dynamic fields through SQL that I can access through the model by default.

Any help would be appreciated.

Rich

UPDATE

An example of my failed attempt to utilise scopes:

default_scope :select => "*, 2 as age"

attr_accessor :age

age is blank, I assume because scopes only deal with limiting, not extending.

like image 473
kim3er Avatar asked Jul 19 '11 00:07

kim3er


2 Answers

kim3er your solution to your problem is simple. Follow these steps:

Loose the attr_accessor :age from your model. You simply don't need it.

Leave the default scope at the Person model: default_scope :select => "*, 2 as age"

Lastly open up a console and try

p = Person.first
p.age
=> 2

When you define a select using as, Rails will automagically add those methods on the instances for you! So the answer to your question:

There must be a way to define dynamic fields through SQL that I can access through the model by default.

is:

Rails

like image 61
Gerry Avatar answered Nov 16 '22 06:11

Gerry


I'm not an expert by any stretch, but it seems you want:

class Person < ActiveRecord::Base

scope :exact_age, lambda { |a| where('age = ?', a) }
scope :age_gt, lambda { |a| where('age > ?', a) }

but that said, I've just started looking at Arel, seems pretty cool

like image 1
Michael K Madison Avatar answered Nov 16 '22 08:11

Michael K Madison