Tolga
Tolga

Reputation: 1357

Rails: How to sort by calculated date difference?

I have a calculated field in my model as follows.

class Products < ApplicationRecord
attr_reader :days

def days
  (Date.today - self.created_at).to_i
end

end

When I try to sort it with, I receive an error.

@products = Product.all.order("days").paginate(:page => params[:page], :per_page => 15)

Error:

PG::UndefinedColumn: ERROR:  column "days" does not exist

I will appreciate if anyone can show me how to sort based on a calculated field?

Upvotes: 1

Views: 713

Answers (3)

Deepesh
Deepesh

Reputation: 6398

I am not sure how you are running this piece of code:

(Date.today - self.created_at).to_i

Because it expects a numeric value with the - sign. I was able to do it like this:

((Time.zone.now - self.created_at) / 1.day).to_i

But the main point is I think you want to order the records by created_at date. So by default it is ascending and you want to display the records which has been recently created first so you can do it like this directly:

Product.all.order(:created_at)

and if you want it in descending order then you can do it like this:

Product.all.order(created_at: :desc)

Still if you need to sort it by an attr_reader the other answers show how you can do it. Just the issue will be that the pagination works on ActiveRecord::Collection rather than array so for will_paginate you may refer here:

Ruby on Rails will_paginate an array

Hope this helps.

Upvotes: 1

Mohanraj
Mohanraj

Reputation: 4200

Rails order clause parameter columb should be exist in the table, does not support for user defined custom attribute in the model. So, you have to use the ruby sort_by method for custom defined attributes like below,

Product.all.sort_by(&:days)

And also you have to change your method to like below,

def days
  DateTime.now.to_i - self.created_at.to_i
end

It will just work but this is not a best practice to sort the records based on custom user defined custom attribute. So, you have to move this logic to sql query itself like below,

Product.all.order("now() - created_at")

It works on postgres, not sure about mysql, please check alternate in mysql if not working.

Upvotes: 2

widjajayd
widjajayd

Reputation: 6253

the problem for your code above is attr_reader :days, meanwhile days you declared as method not variable

here is my idea for your problem

in your model

class Products < ApplicationRecord

    def total_days
      (Date.today - self.created_at).to_i
    end

    def self.sorted_by_days
      Product.all.sort_by(&:total_days).reverse
      # just delete .reverse incase you want ascending
      # I put reverse in case you want to sort descending
    end

end

in your controller

@products = Product.sorted_by_days.paginate(:page => params[:page], :per_page => 15)

Upvotes: 1

Related Questions