logo logo

January 31, 2019 04:17

Rails+heroku(PostgreSQL)で月別アーカイブ

環境

Rails : 5.1.6
ホスティングサービス : heroku
データベース : PostgreSQL
モデル : User hasone Blog, Blog hasmany Articles(以下の記事元のリレーションに倣う)

実現したかったこと

エラー内容はこんな感じ

ActiveRecord::StatementInvalid (PG::UndefinedFunction: ERROR:  function strftime(unknown, timestamp without time zone) does not exist

参考元のページではgemを使って解決されているようでしたが、折角素晴らしい実装だったので、わざわざgemを入れてブラックボックスを増やしたくないと思い、少しハックを行って実現しました。またローカルのデータベースはSQLiteですが、問題なく動作しています。

変更点

エラー箇所はstrftimeメソッドがPostgreSQLに対するSQL文として発行される箇所だったので、該当箇所のみ変更しました。まずはblog.rb

def devide_monthly
    self.articles.pluck(:created_at) \
        .group_by {|timezone| timezone.strftime('%Y%m')}.map {|k, v| [k, v.size]}.sort.reverse
end

上のコードでは

  1. ユーザーに紐づいたArticleオブジェクトに対し、pluckメソッドでcreated_atカラムのみを抽出
  2. group_byメソッドで、各々のtimezoneをstrftimeでフォーマット変換して上でグループ化を実行
  3. mapメソッドでそれぞれの時期キーごとに要素をカウントアップしたhashを作成
  4. sortを実行

という一連の処理になります。pluckを省いて

def devide_monthly
    self.articles.group_by {|article| article.created_at.strftime('%Y%m')}.map {|k, v| [k, v.size]}.sort.reverse
end

としても然程問題なさそうです。次に月別の記事一覧ページに対応するコントローラのアクション(記事元ではBlogsコントローラの#archivesアクション)を以下のようにしました。

def archives  
    @blog = Blog.find(params[:id])
    @yyyymm = params[:yyyymm]
    @articles = @blog.articles.group_by {|article| article.created_at.strftime('%Y%m')[@yyyymm] \
        .sort_by! {|article| article[:created_at]}}.reverse
end

done.

参考