6
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

RailsのActiveRecordで使ってしまうその場しのぎのテクニック5つ

Last updated at Posted at 2018-07-02

はじめに

RailsのActiveRecordでよく使ってしまうテクニックをまとめてみた。
大体、試行錯誤の果てに生まれたり見つけたりしたものなので、パフォーマンスや正しさなどはわからない

全体的にもっとスマートな書き方があったら教えてほしいです。

LEFT JOINに条件を加える

INNER JOINであれば、whereを書けばとりあえず条件は書けるが、LEFT JOINだとそうもいかない
リレーションに条件を加えたり、Arelを使う方法もあるが、どっちも嫌なので

User.joins("LEFT JOIN messages ON(messages.user_id = user.id and messages.opend = 1)")
# eager_loadingしたいときは
User.eager_load(:messages).joins('AND messages.opend = 1')

みたいな感じで書いている。

Rail5からleft_joinsというそのまんまなメソッドが追加されたが、これだとうまくいかなかった。なぞ。

参考: railsでのeager_loadの結合条件の追加はできますか?

NULLや空文字の入ったカラムで並び替えする

NULLが入ったカラムを昇順に並び替えすると、NULLの入ったレコードが先頭に来るが、そうなって欲しくないとき

User.order('users.image IS NULL ASC')
# もしくは
User.order("users.image = '' ")

と書くとNULLや空文字のレコードが下の方に並び替えられる

参考: MySQLのソートで空白の行を下げようとしたらハマった話

countした値で並び替えたい

記事(article) has many コメント(comments)みたいなリレーションで、記事についたコメントの件数で並び替えたいとかよくある

  scope :popular, -> {
    article_ids = Article.
      joins(:comments).
      group('comments.article_id').
      order('count_commnets_article_id desc').
      count('comments.article_id').
      keys
    where(id: article_ids).order(['FIELD(articles.id, ?)', article_ids])
  }

countを使うと、prefixにcount_がつくようなのでそれでorder
{ article_id => count_comments_article_id }のハッシュが返されるので、keysでarticle_idだけ取り出して、FIELD関数で並び替え

これももっとうまい方法がある気がするが慣れるとササッとかけるし、応用が効くのでつい頼ってしまうやつ

スコープをモジュール化する

module readable
  def self.included(base)
    base.class_eval do
      scope :already_read, -> {
        where(read: true)
      }
    end
  end
end

上のコードはmoduleがincludeされたときに、class_evalメソッドでブロック内をクラスメソッドのように扱ってるらしい
scopeって要はクラスメソッドの糖衣構文だったので確かそう
特異クラスとかそのへんはちょっと勉強中

ランダムなレコードを1件取得する

MySQLであればorder("RAND()")とかで済ませてしまうのだけれど、DB移行なんて滅多にないものの、わざわざ依存を作ってしまうのは良くないと思い始めた。

Blog.find(Blog.pluck(:id).sample)

というわけで読みやすさなども兼ねて上記で落ち着いている。大体はテストで使う。

参考: Rails ActiveRecordでランダムにレコードを1件取得する

(おまけ)uniqがdeprecatedになってた

ちょっと本題からは逸れるが、気づかなくてハマってしまったので

uniqはrubyのArrayクラスとかにもあるメソッドなんだけど、railsではdistinctみたいな使い方もできる
その2つの使い分けをどこでしているかは調べていないが、空のActiveRecord::Relationに対してuniqするとArrayが返って、その後のwhere句とかで落ちてしまうみたいなことがあった。

なのでdistinctを使いましょうということでした。

参考:deprecate Relation#uniq use Relation#distinct instead.

6
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?